1 Commits

Author SHA1 Message Date
aa708a34a4 docs: extend CLAUDE.md with cross-session friction notes
Some checks failed
Tests / Run tests on PHP v8.4 (push) Failing after 1s
Tests / Run tests on PHP v8.5 (push) Failing after 0s
Captures four pieces of context that cost time to (re)derive this
session:

- Docker `--entrypoint php` one-liner for ad-hoc PHP that needs the
  codex autoloader (used for redactor smoke tests).
- Pitfall #6: PZ DebugLog-server has two coexisting line shapes
  (B41 with `t:` field, B42 without) — `DebugServerPattern::LINE`
  matches both via an optional group; narrowing it back to B41-only
  silently disables ServerExceptionProblem / ModMissingProblem on
  every B42 log.
- Deployed iblogs lives at bosslogs.indifferentketchup.com and uses
  `main` as its default branch, not `master`. Pinned to ^0.3.0.
- New top-level section for `tools/pz-analyzer/` describing the
  intentional split between the pre-production Qwen-backed
  discovery tool and the production-bound deterministic
  classifier, plus the redact-all wrapper that feeds both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 19:45:26 +00:00

View File

@@ -16,6 +16,12 @@ docker run --rm -v "$(pwd):/app" -w /app -u "$(id -u):$(id -g)" composer:latest
Use `$(pwd)` or an absolute path — bare `$PWD` has misfired here, mounting nothing and silently no-op'ing the run. Use `$(pwd)` or an absolute path — bare `$PWD` has misfired here, mounting nothing and silently no-op'ing the run.
For ad-hoc PHP that needs the codex autoloader (e.g. running `ProjectZomboidRedactor::redact()` over a directory of log files, or eyeballing analyser output), override the entrypoint:
```
docker run --rm --entrypoint php -v "$(pwd):/app" -w /app -u "$(id -u):$(id -g)" composer:latest -r '<php source>'
```
## Common commands ## Common commands
- All tests: `composer test` (= `phpunit test/tests` per `composer.json`) - All tests: `composer test` (= `phpunit test/tests` per `composer.json`)
@@ -88,6 +94,16 @@ At minimum: (1) entry count after `parse()` matches the synthetic fixture's line
`iblogs` (sibling repo at `/opt/iblogs`, package `indifferentketchup/iblogs`, fork of `aternosorg/mclogs`) is the primary consumer of codex via a Composer `vcs` repository entry pinned to the latest minor tag. Public-API changes in `src/{Detective,Log,Printer,Util}/*.php` and `src/Analysis/*.php` propagate there; when modifying those types, sanity-check the iblogs call sites at `/opt/iblogs/src/{Detective.php,Log.php,Printer/Printer.php,Printer/FormatModification.php,Api/Response/CodexLogResponse.php}` and the stub class at `/opt/iblogs/src/Data/Deobfuscator.php`. `iblogs` (sibling repo at `/opt/iblogs`, package `indifferentketchup/iblogs`, fork of `aternosorg/mclogs`) is the primary consumer of codex via a Composer `vcs` repository entry pinned to the latest minor tag. Public-API changes in `src/{Detective,Log,Printer,Util}/*.php` and `src/Analysis/*.php` propagate there; when modifying those types, sanity-check the iblogs call sites at `/opt/iblogs/src/{Detective.php,Log.php,Printer/Printer.php,Printer/FormatModification.php,Api/Response/CodexLogResponse.php}` and the stub class at `/opt/iblogs/src/Data/Deobfuscator.php`.
The deployed iblogs instance lives at `bosslogs.indifferentketchup.com` (production renders the same code path as the local dev server on port 4217). iblogs's default branch is `main`, not `master`. iblogs's `composer.json` constraint is currently `^0.3.0`; cutting a v0.4.x will require widening that.
## Out-of-library tools (`tools/pz-analyzer/`)
Python utilities alongside the Composer package, not on the PSR-4 autoload surface. Two artefacts coexist by design — the deterministic classifier is the production target; the Qwen tool is the developer's discovery aid for shapes the deterministic side hasn't captured yet.
- **`pz_redact_all.sh`** — one-shot Docker wrapper. Runs `ProjectZomboidRedactor` over `.scratch/pz/Logs/` and writes `.scratch/pz/Logs.redacted/`. Both Python tools below consume the redacted directory.
- **`pz_error_analysis.py`** — *pre-production*, Qwen-backed. Sends residual log shapes to the local Qwen endpoint at `http://100.101.41.16:8401/v1` (sam-desktop, model `qwen3.6-35b-a3b`) for natural-language classification with category / cause / fix output. Requires the `openai` package; in practice run via `/opt/analytics/.venv/bin/python` which has it installed.
- **`pz_parser.py` + `pz_classify.py`** — *production-bound deterministic classifier*. Stdlib only. Mirrors the patterns from `paraxaQQ/pzmm`'s `core/inspector.py` (Lua mod-marker attribution, bidirectional stack collection, file:line extraction, cause-chain unwinding, engine-noise tagging) plus a two-level signature scheme (`pattern_id` + `signature`). Designed to inform a future PHP port to `LuaErrorAnalyser` / `ModAttributionAnalyser` under `src/Analyser/ProjectZomboid/`. 32 stdlib `unittest` tests under `tools/pz-analyzer/tests/`; invocation: `python3 -m unittest discover -s tools/pz-analyzer/tests`.
## Pitfalls ## Pitfalls
1. **`PatternParser` is incompatible with named regex groups.** PHP's `preg_match` returns named groups *plus* their numeric duplicates in the same array; `PatternParser`'s foreach iterates both and throws on the string-key entries. Convention: `LINE` regexes (used by the parser) use **unnamed** groups with field order documented in the Pattern class's docblock. Named groups are fine inside extractor regexes invoked from analysers, since `PatternAnalyser` hands the whole match array to `Insight::setMatches`. 1. **`PatternParser` is incompatible with named regex groups.** PHP's `preg_match` returns named groups *plus* their numeric duplicates in the same array; `PatternParser`'s foreach iterates both and throws on the string-key entries. Convention: `LINE` regexes (used by the parser) use **unnamed** groups with field order documented in the Pattern class's docblock. Named groups are fine inside extractor regexes invoked from analysers, since `PatternAnalyser` hands the whole match array to `Insight::setMatches`.
@@ -95,6 +111,7 @@ At minimum: (1) entry count after `parse()` matches the synthetic fixture's line
3. **`Level::fromString()` defaults to `Level::INFO` for unknown tokens.** Project Zomboid log levels map: `LOG`/`INFO` → INFO; `WARN` → WARNING; `ERROR` → ERROR. 3. **`Level::fromString()` defaults to `Level::INFO` for unknown tokens.** Project Zomboid log levels map: `LOG`/`INFO` → INFO; `WARN` → WARNING; `ERROR` → ERROR.
4. **`PatternParser` matches array** must declare a match-type for **every** capture group in the regex (`TIME`, `LEVEL`, or `PREFIX`); otherwise the parser throws on the unmapped index. Use non-capturing groups `(?:...)` for fields you want to skip. 4. **`PatternParser` matches array** must declare a match-type for **every** capture group in the regex (`TIME`, `LEVEL`, or `PREFIX`); otherwise the parser throws on the unmapped index. Use non-capturing groups `(?:...)` for fields you want to skip.
5. **`ProjectZomboidRedactor` pass order is mandatory.** `PLAYER_AFTER_STEAMID_REGEX` anchors on the already-redacted Steam ID placeholder — it will not match raw Steam IDs. Do NOT swap the Steam ID and player-name passes, and do NOT stub out the Steam ID pass while leaving the player-name pass enabled. 5. **`ProjectZomboidRedactor` pass order is mandatory.** `PLAYER_AFTER_STEAMID_REGEX` anchors on the already-redacted Steam ID placeholder — it will not match raw Steam IDs. Do NOT swap the Steam ID and player-name passes, and do NOT stub out the Steam ID pass while leaving the player-name pass enabled.
6. **Two PZ DebugLog-server line formats coexist.** B41 emits `[ts] LEVEL: Subsystem f:N, t:N, st:N,N,N,N>`; B42 (build 42.17 onward) dropped the `t:` microsecond field and tightened spacing to `f:N st:N,N,N,N>`. `DebugServerPattern::LINE` matches both via `(?:,\s+t:\d+)?,?` — preserve that optional group when editing or B42 logs silently fail to parse, leaving entries level-less and analysers (`ServerExceptionProblem`, `ModMissingProblem`) silently dormant. Fixtures cover both: `debug-server-minimal.txt` (B41), `debug-server-42x-minimal.txt` (B42).
## Workflow conventions ## Workflow conventions