github devswha/patina v4.1.0
v4.1.0 — Browser diff page, ko advisory analyzer, calibration fixes

latest releases: v8.3.1, v8.3.0, v8.2.1...
3 months ago

Browser diff page, Korean post-editese advisory analyzer, and detector calibration fixes.

Semver rationale: minor — adds the --browser rewrite diff page, the Korean post-editese advisory analyzer surfaced through analyzeText, and an optional structural-classifier scoring hook; the rest is bug fixes to Korean detection rules, local-CLI backends, score-prompt reliability, and npm packaging. Note: 4.0.1 was tagged in this file but never published, so npm consumers upgrade straight from 4.0.0 and pick up its patina-cli bin alias here.

Added

  • --browser rewrite add-on: rewrites one local file (stdout stays byte-for-byte identical for the selected --format), then writes a self-contained local HTML before/after diff page — side-by-side text, changed-block highlights, deterministic score summaries, and a best-effort diff-explanation backend call (one extra model call; explanation failure never fails the rewrite). Rejects stdin/--batch/URLs/non-rewrite modes; if the browser cannot be opened, the saved HTML path is printed on stderr.
  • Korean post-editese advisory analyzer (koPostEditese.v1): deterministic descriptive metrics (lexical, endings, interference, rhythm) surfaced through analyzeText as advisory-only metadata — never folded into the hot verdict or the score.
  • Optional structural-classifier scoring hook: config stylometry.structural_model.path (or the PATINA_STRUCTURAL_MODEL env var) can point at a local structural model; when a loaded model marks text hot, the deterministic score gets a 70-point floor and a structuralClassifier band (available/hot/score/floor) appears in the deterministic-score JSON. Without a model, behavior is unchanged.

Fixed

  • Korean koPostEditese no longer misclassifies regular formal -ㅂ니다 / -ㅂ니까 endings (됩니다, 표시됩니다, 합니까…) as declarative -다 style, so clean 합쇼체 prose is no longer pushed toward register-changing rewrites.

  • Translationese a16-pronoun-literal no longer fires on ordinary nouns ending in (로그/버그/태그) or on 그녀석/그것참; it now requires eojeol boundaries on both sides.

  • Translationese t2-by-passive now matches the common fused passive forms (된다/됩니다/될/진다) that the previous jamo alternation silently missed.

  • Deterministic audit backstop now respects each rule's minCount (e.g. c11-connective-comma no longer surfaces on a single match).

  • Local-CLI backends (claude/gemini/kimi) decode stdout as streaming UTF-8, fixing silent corruption of multi-byte CJK output split across pipe reads; all four backends now handle stdin EPIPE instead of crashing the process when a child exits before draining a large prompt.

  • Ouroboros loop strips [BODY]/[SELF_AUDIT] tags before scoring and re-feeding, returns the best-scoring text paired with its score, and treats a failed MPS scorer as a floor violation (fail closed, matching fidelity).

  • Rewrite tone-footer removal anchors to the final --- block, so a markdown thematic break in the body no longer truncates everything after it.

  • --config <file> now wins over an ambient ./.patina.yaml / ~/.patina.yaml (reproducible runs); config merge is guarded against prototype pollution.

  • Mutually exclusive output modes (--diff / --audit / --score / --ouroboros) are now rejected up front instead of silently resolving to one mode (which could make a --score CI gate always exit 0).

  • Discourse tells (fake-candor openers / thematic breaks) are now attributed to the paragraphs that carry them (#391), so flagged paragraphs enter rewrite scope and reach the deterministic score through the hot ratio — matching the playground. The interim document-level 35-point score floor (added and removed within this release; never published) is gone; the ≥2/≥3 density gates are unchanged. Discourse-hot paragraphs also carry signal strength (tell count normalized by the density gate), keeping the signal-score ranking leg consistent with the hot verdict (no hot-with-zero-signal rows). Prose gates (precommit-score/dogfood) keep their hot-prose-ratio semantics: bare --- divider pseudo-paragraphs are excluded from the gate ratio, while divider spam still reaches the mdx ranking through flooredScore.

  • npm package now ships the scripts behind the benchmark:rebaseline:generate-modern, benchmark:rebaseline:claim-manifest, benchmark:rebaseline:fp-fixtures, and qa:mdx npm scripts (rebaseline-generate-modern.mjs, rebaseline-build-claim-manifest.mjs, fp-fixture-export.mjs, qa/mdx-score.mjs), which previously failed with MODULE_NOT_FOUND for npm consumers (#411).

Changed

  • --format text output no longer appends the Tone: <tone> (<source>) trailer line.
  • --score/scoreText prompts now embed per-pack pattern counts and a full catalog digest, and scoreText follows a single strict-JSON output contract (with strip options and a flooredScore field); the patina-score prose gate strips paired emphasis markers only (so URL-leakage signals like utm_source=chatgpt.com survive) and scores through scoreText with the lexicon channel and canonical floors — gate scores can shift slightly vs 4.0.0.
  • kimi-cli backend runs with --max-steps-per-turn 20 (up from 1). It stays in non-interactive --print mode with no --yolo, so the agent cannot auto-approve shell/file tools — the extra steps only cover reasoning/formatting within a turn (verified that injected tool-use instructions in user text do not execute).
  • Internal: src/cli.js decomposed into per-concern modules (src/cli/args.js, batch.js, input.js, score-gate.js, run.js#409, #413, #414), and the generated docs/API.md no longer claims boilerplate @throws on functions that cannot throw.

Don't miss a new patina release

NewReleases is sending notifications on new releases.