Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/multiplatform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
run: python -m pip install -r requirements.txt

- name: Compile launchers and source
run: python -m compileall -q src run_batllm.py run_tests.py create_release_bundles.py create_homebrew_formula.py
run: python -m compileall -q src run_batllm.py run_tests.py create_release_bundles.py create_homebrew_formula.py run_game_analyzer.py validate_packaging_smoke.py

- name: Run non-live test suite
run: python -m pytest -q src/tests --ignore=src/tests/test_homebrew_packaging.py
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ prefer-stubs=no

# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.13
py-version=3.12

# Discover python modules and packages in the file system subtree.
recursive=no
Expand Down
24 changes: 21 additions & 3 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# BatLLM Status

Last updated: 2026-05-10 17:21
Last updated: 2026-05-23
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore STATUS timestamp to required YYYY-MM-DD HH:MM format

STATUS.md now uses Last updated: 2026-05-23 (date only), but the repository rule in AGENTS.md requires the exact format Last updated: YYYY-MM-DD HH:MM and treats incorrect formatting as a blocking documentation error; this also appears at the final Last updated line, so automation or manual audits that rely on the mandated timestamp format can no longer validate recency correctly.

Useful? React with 👍 / 👎.


BatLLM is a Python/Kivy research, education, and game project for exploring AI-mediated play, prompt quality, LLM behaviour, and local-model workflows. The repository currently contains a playable local desktop game, a standalone read-only Game Analyzer, local Ollama lifecycle and model-management helpers routed through `modelito`, release-bundle tooling, Homebrew formula generation, generated API reference artefacts, and maintained user/developer documentation.

Expand Down Expand Up @@ -63,6 +63,19 @@ python run_tests.py full
- `KIVY_WINDOW=mock`, `KIVY_NO_ARGS=1`, `KIVY_NO_CONSOLELOG=1`: useful for headless CI/test runs.
- `BATLLM_RUN_OLLAMA_SMOKE=1`: enables gated Ollama smoke tests.

## Bug Fix Audit — 2026-05-23

A repository-wide bug audit was performed on 2026-05-23, reading every Python source file, CI configuration, and tooling script. Six confirmed bugs were found and fixed; 148 tests pass post-fix.

### Fixes Applied

- **`src/util/utils.py`** — `markup()`: removed inverted `else: font_size = 18` branch that silently overrode every valid caller-supplied `font_size` with a hardcoded value.
- **`src/configs/app_config.py`** — `AppConfig.set()`: moved the section-creation guard outside the `isinstance` check so standard-type values (`str`/`int`/`float`/`bool`) no longer raise `KeyError` when the section does not yet exist.
- **`src/game/ollama_connector.py`** — `load_options()`: removed dead-code line `self.stop = _maybe_int(config.get("llm", "stop"))` that was immediately overwritten by the correct assignment two lines later (and incorrectly applied `_maybe_int` to a value that should be a list of stop strings).
- **`src/configs/configurator.py`** — Narrowed all six bare `except:` clauses to `except (ValueError, TypeError):`, `except ValueError:`, or `except Exception:` as appropriate; bare `except:` clauses swallow `SystemExit` and `KeyboardInterrupt`.
- **`.github/workflows/multiplatform.yml`** — Added `run_game_analyzer.py` and `validate_packaging_smoke.py` to the CI `compileall` step so syntax errors in those launchers are caught by CI.
- **`.pylintrc`** — Changed `py-version=3.13` to `py-version=3.12` to match the supported range `>=3.10,<3.13`.

## Thorough Audit Snapshot

This status update followed a repository-wide audit on 2026-05-09. The audit inspected tracked files, top-level project structure, maintained documentation, source modules, tests, packaging tools, CI workflows, configuration defaults, generated documentation artefacts, and current git state.
Expand Down Expand Up @@ -186,7 +199,12 @@ This status update followed a repository-wide audit on 2026-05-09. The audit ins

## Tests And Verification Status

### Latest Commands Run For This Audit
### Latest Commands Run For This Audit (2026-05-23 Bug Fix Audit)

- Repository-wide source read: all Python files in `src/`, root launchers, `tools/`, `scripts/`, CI workflows, `requirements.txt`, `pytest.ini`, `.pylintrc`, and packaging files read and cross-referenced by three parallel agents.
- `python -m pytest -q src/tests --ignore=src/tests/test_homebrew_packaging.py` -> `148 passed, 2 skipped`; run after all six fixes were applied.

### Latest Commands Run For Previous Audit (2026-05-09)

- `git status --short` -> passed; confirmed the worktree was clean before edits.
- Repository audit commands using `rg`, `git ls-files`, `find`, `sed`, and `wc` -> passed; checked tracked inventory, maintained docs, source modules, config files, TODO markers, stale version/path references, ignored local artefacts, generated docs, and test inventory.
Expand Down Expand Up @@ -275,4 +293,4 @@ The previous status report recorded these successful checks from the same releas
- Design the 2.0 server contract before adding web or repository-backed prompt/game sharing.
- Add broader tests for malformed model responses, slow startup, missing models, session compatibility, analyzer edge cases, and packaged first-run behaviour.

Last updated: 2026-05-10 17:21
Last updated: 2026-05-23
5 changes: 2 additions & 3 deletions src/configs/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ def set(self, section, key, value):
If the section does not exist, it creates it.
"""

if not isinstance(value, (str, int, float, bool)):
if section not in self._config:
self._config[section] = {}
if section not in self._config:
self._config[section] = {}

self._config[section][key] = value

Expand Down
12 changes: 6 additions & 6 deletions src/configs/configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def safe_float(v: str) -> Optional[float]:
return None
try:
return float(v)
except:
except (ValueError, TypeError):
return None

def safe_int(v: str) -> Optional[int]:
Expand All @@ -63,7 +63,7 @@ def safe_int(v: str) -> Optional[int]:
return None
try:
return int(v)
except:
except (ValueError, TypeError):
return None

def safe_any(v: str) -> Optional[Any]:
Expand All @@ -72,7 +72,7 @@ def safe_any(v: str) -> Optional[Any]:
return None
try:
return json.loads(v)
except:
except (ValueError, TypeError):
return v

SNAPSHOT_HTML_TMPL = (
Expand Down Expand Up @@ -235,13 +235,13 @@ def apply_to_config(self, cfg: Dict[str, Any]) -> Dict[str, Any]:
else:
try:
val = json.loads(raw)
except:
except (ValueError, TypeError):
if re.fullmatch(r'-?\d+', raw):
val = int(raw)
else:
try:
val = float(raw)
except:
except ValueError:
val = raw
sec_dict[key] = val
cfg[sec] = sec_dict
Expand Down Expand Up @@ -549,7 +549,7 @@ def stop(self):
self._stop_flag = True
try:
self.app_ref._stop_models(None)
except:
except Exception:
pass
def generate(self):
if self._gen_thread and self._gen_thread.is_alive():
Expand Down
1 change: 0 additions & 1 deletion src/game/ollama_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ def load_options(self, force: bool = False) -> None:

self.num_thread = _maybe_int(config.get("llm", "num_thread"))
self.seed = _maybe_int(config.get("llm", "seed"))
self.stop = _maybe_int(config.get("llm", "stop"))

self.num_ctx = _maybe_int(config.get("llm", "num_ctx"))
self.num_predict = _maybe_int(config.get("llm", "num_predict"))
Expand Down
2 changes: 0 additions & 2 deletions src/util/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,6 @@ def markup(text: str, font_size: Optional[int] = None, color="#000000", bold=Fal

if font_size <= 0 or not isinstance(font_size, int):
font_size = config.get("ui", "font_size")
else:
font_size = 18

if not color.startswith("#") or len(color) != 7:
color = "#000000" # default to black if invalid
Expand Down
Loading