From be5c3477d317ad0c37c845cee3aa6855c7713af9 Mon Sep 17 00:00:00 2001
From: devswha
Date: Wed, 5 Aug 2026 09:08:29 +0900
Subject: [PATCH 1/3] =?UTF-8?q?feat!:=20voice-axis=20cutover=20=E2=80=94?=
=?UTF-8?q?=20Document=20Type=20/=20Persona=20/=20Register=20replace=20pro?=
=?UTF-8?q?file,=20tone,=20and=20formality?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
17 Document Types own genre and pattern policy through a validated
frontmatter contract; Persona v2 stays the optional reusable voice axis;
Register selects casual or professional delivery. --profile/--tone/
--formality are rejected with migration errors. Examples, docs, prompt
snapshots, live-quality fixtures, and the persona/document-type test
surfaces move with it.
---
.cursor/rules/patina.md | 43 ++-
.patina.default.yaml | 45 +--
CHANGELOG.md | 4 +
CONTRIBUTING.md | 25 +-
CONTRIBUTING_KR.md | 22 +-
INSTALLATION.md | 6 +-
README.md | 46 ++-
README_JA.md | 37 ++-
README_KR.md | 42 ++-
README_ZH.md | 37 ++-
SKILL.md | 216 ++++----------
agents/patina-fidelity-auditor.md | 15 +-
agents/patina-naturalness-reviewer.md | 2 +-
assets/demo/README.md | 4 +-
core/scoring.md | 61 ++--
core/standalone-prompt.md | 62 ++--
core/voice.md | 71 ++---
docs/API.md | 172 +++++------
docs/ARCHITECTURE.md | 148 ++++-----
docs/CLI.md | 122 +++++---
docs/COOKBOOK.md | 76 ++---
docs/DEMO.md | 4 +-
docs/EXAMPLES.md | 6 +-
docs/EXAMPLES_KR.md | 6 +-
docs/FALSE-POSITIVES.md | 2 +-
docs/FAQ.md | 38 ++-
docs/FAQ_KR.md | 36 ++-
docs/FLAG-PARITY.md | 8 +-
docs/GLOSSARY.md | 47 +--
docs/ROADMAP.md | 39 +--
docs/agents.md | 4 +-
docs/integrations/playground.md | 5 +-
{profiles => document-types}/academic.md | 22 +-
{profiles => document-types}/blog.md | 28 +-
document-types/casual-conversation.md | 92 ++++++
{profiles => document-types}/code-comment.md | 26 +-
.../commit-message.md | 22 +-
document-types/default.md | 34 +++
{profiles => document-types}/email.md | 26 +-
{profiles => document-types}/formal.md | 36 ++-
{profiles => document-types}/instructional.md | 38 ++-
{profiles => document-types}/legal.md | 22 +-
{profiles => document-types}/marketing.md | 22 +-
{profiles => document-types}/medical.md | 22 +-
{profiles => document-types}/namuwiki.md | 26 +-
{profiles => document-types}/narrative.md | 22 +-
{profiles => document-types}/release-notes.md | 22 +-
{profiles => document-types}/social.md | 30 +-
{profiles => document-types}/technical.md | 22 +-
examples/README.md | 14 +-
.../namuwiki-01-after.md | 4 +-
.../namuwiki-01-before.md | 4 +-
examples/document-types/namuwiki-02-after.md | 11 +
.../namuwiki-02-before.md | 4 +-
examples/profiles/namuwiki-02-after.md | 11 -
examples/{tones => rewrite-axes}/RESULTS.md | 30 +-
examples/rewrite-axes/academic-en.md | 24 ++
examples/rewrite-axes/academic-ko.md | 24 ++
examples/rewrite-axes/casual-en.md | 23 ++
examples/rewrite-axes/casual-ko.md | 23 ++
examples/rewrite-axes/instructional-en.md | 27 ++
examples/rewrite-axes/instructional-ko.md | 27 ++
examples/rewrite-axes/marketing-en.md | 24 ++
examples/rewrite-axes/marketing-ko.md | 23 ++
examples/rewrite-axes/narrative-en.md | 23 ++
examples/rewrite-axes/narrative-ko.md | 23 ++
examples/rewrite-axes/professional-en.md | 23 ++
examples/rewrite-axes/professional-ko.md | 23 ++
examples/tones/academic-en.md | 26 --
examples/tones/academic-ko.md | 26 --
examples/tones/casual-en.md | 26 --
examples/tones/casual-ko.md | 25 --
examples/tones/instructional-en.md | 29 --
examples/tones/instructional-ko.md | 30 --
examples/tones/marketing-en.md | 25 --
examples/tones/marketing-ko.md | 25 --
examples/tones/narrative-en.md | 25 --
examples/tones/narrative-ko.md | 25 --
examples/tones/professional-en.md | 25 --
examples/tones/professional-ko.md | 25 --
package.json | 2 +-
personas/en/blog-essay.md | 15 +-
personas/en/natural-en.md | 15 +-
personas/en/preserve.md | 44 ---
personas/en/technical-explainer.md | 15 +-
personas/ja/blog-essay.md | 15 +-
personas/ja/natural-ja.md | 15 +-
personas/ja/preserve.md | 44 ---
personas/ko/blog-essay.md | 17 +-
personas/ko/natural-ko.md | 16 +-
personas/ko/pragmatic-founder.md | 17 +-
personas/ko/preserve.md | 44 ---
personas/ko/soft-professional.md | 17 +-
personas/ko/technical-explainer.md | 17 +-
personas/zh/blog-essay.md | 15 +-
personas/zh/natural-zh.md | 15 +-
personas/zh/preserve.md | 44 ---
playground/README.md | 2 +-
playground/chatgpt.js | 38 ++-
playground/index.html | 16 +-
playground/rewrite-client.js | 8 +-
playground/src/web-rewrite-contract.js | 52 +++-
profiles/casual-conversation.md | 67 -----
profiles/default.md | 23 --
scripts/check-retired-concepts.mjs | 26 +-
scripts/check-v6.4-preflight-hold.mjs | 3 +
scripts/generate-api-docs.mjs | 20 +-
scripts/iterative-rewrite-baseline.mjs | 16 +-
scripts/persona-ablation.mjs | 17 +-
scripts/render-demo-gif.py | 2 +-
scripts/render-preview-demo.mjs | 6 +-
scripts/research/rewrite-efficacy-pilot.mjs | 7 +-
scripts/rewrite-ab.mjs | 8 +-
src/cli.js | 2 +-
src/cli/args.js | 154 +++++-----
src/cli/run.js | 282 +++++++-----------
src/commands/persona.js | 57 +---
src/config.js | 124 ++++----
src/features/index.js | 12 +-
src/features/short-form.js | 24 +-
src/loader.js | 130 ++++++--
src/output.js | 104 +++----
src/personas/compose.js | 68 ++---
src/personas/gates.js | 68 +----
src/personas/resolve.js | 40 +--
src/personas/schema.js | 116 ++-----
src/preview/render.js | 28 +-
src/prompt-builder.js | 277 ++++++++++-------
src/scoring.js | 30 +-
src/security.js | 27 +-
src/verify.js | 20 +-
src/web-config.js | 9 +
src/web-rewrite-contract.js | 52 +++-
src/web-rewrite-stream.js | 6 +-
src/web-rewrite.js | 60 ++--
tests/e2e/cli-adoption.test.js | 12 +-
tests/e2e/cli-api.test.js | 12 +-
tests/e2e/cli-persona.test.js | 147 ++++-----
tests/e2e/security.test.js | 34 +--
tests/fixtures/live-quality/en/academic-01.md | 2 +-
tests/fixtures/live-quality/en/blog-01.md | 2 +-
tests/fixtures/live-quality/en/chat-01.md | 2 +-
tests/fixtures/live-quality/en/email-01.md | 2 +-
tests/fixtures/live-quality/en/howto-01.md | 2 +-
.../live-quality/en/instructional-01.md | 2 +-
.../fixtures/live-quality/en/marketing-01.md | 2 +-
tests/fixtures/live-quality/en/news-01.md | 4 +-
tests/fixtures/live-quality/en/product-01.md | 2 +-
.../live-quality/en/public-docs-01.md | 2 +-
tests/fixtures/live-quality/en/social-01.md | 2 +-
tests/fixtures/live-quality/ko/academic-01.md | 2 +-
tests/fixtures/live-quality/ko/blog-01.md | 2 +-
tests/fixtures/live-quality/ko/chat-01.md | 2 +-
tests/fixtures/live-quality/ko/email-01.md | 2 +-
tests/fixtures/live-quality/ko/howto-01.md | 2 +-
.../live-quality/ko/instructional-01.md | 2 +-
.../fixtures/live-quality/ko/marketing-01.md | 2 +-
tests/fixtures/live-quality/ko/news-01.md | 2 +-
tests/fixtures/live-quality/ko/product-01.md | 2 +-
.../live-quality/ko/public-docs-01.md | 2 +-
tests/fixtures/live-quality/ko/social-01.md | 2 +-
tests/fixtures/prompt-snapshots/audit.md | 48 +--
tests/fixtures/prompt-snapshots/diff.md | 56 ++--
.../prompt-snapshots/rewrite-minimal.md | 50 +++-
.../prompt-snapshots/rewrite-signals.md | 87 +++---
.../prompt-snapshots/rewrite-strict.md | 87 +++---
tests/fixtures/prompt-snapshots/score.md | 50 ++--
tests/quality/README.md | 2 +-
tests/quality/live-quality.mjs | 18 +-
tests/quality/scorer-benchmark.mjs | 8 +-
tests/unit/browser-diff.test.js | 10 +-
tests/unit/config.test.js | 10 +-
tests/unit/document-signals.test.js | 6 +-
...s => document-type-pattern-policy.test.js} | 32 +-
tests/unit/heading-preservation.test.js | 6 +-
tests/unit/issue-527-fixes.test.js | 17 +-
tests/unit/iterative-rewrite-baseline.test.js | 8 +-
tests/unit/live-quality.test.js | 15 +-
tests/unit/loader.test.js | 148 ++++++---
tests/unit/persona-ablation-live.test.js | 12 +-
tests/unit/persona-args.test.js | 23 +-
tests/unit/persona-command.test.js | 26 +-
tests/unit/persona-gates.test.js | 156 ++--------
tests/unit/persona-match.test.js | 2 +-
tests/unit/persona-natural-ko.test.js | 18 +-
tests/unit/persona-packaging.test.js | 10 +-
tests/unit/persona-resolver.test.js | 24 +-
tests/unit/persona-schema.test.js | 42 +--
tests/unit/persona-seed.test.js | 34 +--
tests/unit/playground-pro.test.js | 2 +-
tests/unit/preview.test.js | 8 +-
tests/unit/prompt-builder.persona.test.js | 104 ++++---
tests/unit/prompt-builder.snapshot.test.js | 58 ++--
tests/unit/{tone.test.js => register.test.js} | 203 ++++---------
tests/unit/scoring.test.js | 56 ++--
tests/unit/short-form.test.js | 36 +--
tests/unit/threshold-parity.test.js | 6 +-
tests/unit/transform-options.test.js | 79 +++--
tests/unit/v6.4-preflight-hold.test.js | 2 +-
tests/unit/verify.test.js | 2 +-
tests/unit/web-config.test.js | 11 +-
tests/unit/web-deploy-invariants.test.js | 8 +-
tests/unit/web-rewrite-stream.redteam.test.js | 2 +-
tests/unit/web-rewrite.redteam.test.js | 41 +--
tests/unit/web-rewrite.test.js | 59 ++--
tests/unit/xliff-cli.redteam.test.js | 2 +-
tests/unit/xliff-cli.test.js | 18 +-
tests/unit/xliff-crossfile-dedup.test.js | 2 +-
vercel.json | 2 +-
209 files changed, 3349 insertions(+), 3616 deletions(-)
rename {profiles => document-types}/academic.md (71%)
rename {profiles => document-types}/blog.md (69%)
create mode 100644 document-types/casual-conversation.md
rename {profiles => document-types}/code-comment.md (79%)
rename {profiles => document-types}/commit-message.md (81%)
create mode 100644 document-types/default.md
rename {profiles => document-types}/email.md (62%)
rename {profiles => document-types}/formal.md (69%)
rename {profiles => document-types}/instructional.md (62%)
rename {profiles => document-types}/legal.md (75%)
rename {profiles => document-types}/marketing.md (73%)
rename {profiles => document-types}/medical.md (71%)
rename {profiles => document-types}/namuwiki.md (78%)
rename {profiles => document-types}/narrative.md (73%)
rename {profiles => document-types}/release-notes.md (83%)
rename {profiles => document-types}/social.md (55%)
rename {profiles => document-types}/technical.md (69%)
rename examples/{profiles => document-types}/namuwiki-01-after.md (84%)
rename examples/{profiles => document-types}/namuwiki-01-before.md (78%)
create mode 100644 examples/document-types/namuwiki-02-after.md
rename examples/{profiles => document-types}/namuwiki-02-before.md (77%)
delete mode 100644 examples/profiles/namuwiki-02-after.md
rename examples/{tones => rewrite-axes}/RESULTS.md (71%)
create mode 100644 examples/rewrite-axes/academic-en.md
create mode 100644 examples/rewrite-axes/academic-ko.md
create mode 100644 examples/rewrite-axes/casual-en.md
create mode 100644 examples/rewrite-axes/casual-ko.md
create mode 100644 examples/rewrite-axes/instructional-en.md
create mode 100644 examples/rewrite-axes/instructional-ko.md
create mode 100644 examples/rewrite-axes/marketing-en.md
create mode 100644 examples/rewrite-axes/marketing-ko.md
create mode 100644 examples/rewrite-axes/narrative-en.md
create mode 100644 examples/rewrite-axes/narrative-ko.md
create mode 100644 examples/rewrite-axes/professional-en.md
create mode 100644 examples/rewrite-axes/professional-ko.md
delete mode 100644 examples/tones/academic-en.md
delete mode 100644 examples/tones/academic-ko.md
delete mode 100644 examples/tones/casual-en.md
delete mode 100644 examples/tones/casual-ko.md
delete mode 100644 examples/tones/instructional-en.md
delete mode 100644 examples/tones/instructional-ko.md
delete mode 100644 examples/tones/marketing-en.md
delete mode 100644 examples/tones/marketing-ko.md
delete mode 100644 examples/tones/narrative-en.md
delete mode 100644 examples/tones/narrative-ko.md
delete mode 100644 examples/tones/professional-en.md
delete mode 100644 examples/tones/professional-ko.md
delete mode 100644 personas/en/preserve.md
delete mode 100644 personas/ja/preserve.md
delete mode 100644 personas/ko/preserve.md
delete mode 100644 personas/zh/preserve.md
delete mode 100644 profiles/casual-conversation.md
delete mode 100644 profiles/default.md
rename tests/unit/{profile-pattern-overrides.test.js => document-type-pattern-policy.test.js} (65%)
rename tests/unit/{tone.test.js => register.test.js} (55%)
diff --git a/.cursor/rules/patina.md b/.cursor/rules/patina.md
index 565a816..e4ed43a 100644
--- a/.cursor/rules/patina.md
+++ b/.cursor/rules/patina.md
@@ -4,15 +4,15 @@
Patina is a **Claude Code skill** that detects and removes AI writing patterns from Korean, English, Chinese, and Japanese text. It rewrites AI-sounding text into natural, human-like prose while preserving meaning through the Meaning Preservation System (MPS).
-The project uses a **plugin architecture**: patterns are plugins (`patterns/{lang}-*.md`), profiles are themes (`profiles/*.md`). Inspired by oh-my-zsh.
+The project uses a **plugin architecture**: pattern packs (`patterns/{lang}-*.md`), Document Type policies (`document-types/*.md`), and optional Persona v2 voice definitions (`personas//*.md`).
## File Structure Conventions
- **Pattern packs**: `patterns/{lang}-{category}.md` (e.g., `en-content.md`, `ko-language.md`)
- Must have valid YAML frontmatter: `pack`, `language`, `name`, `version`, `patterns`
- Pattern definitions use `### N. Pattern Name` headings
-- **Profiles**: `profiles/{name}.md` (e.g., `blog.md`, `academic.md`)
- - Define `voice-overrides` and `pattern-overrides`
+- **Document Types**: `document-types/{name}.md` — genre, structure, and `pattern-overrides`
+- **Personas**: `personas/{lang}/{name}.md` — optional voice-only Persona v2 frontmatter
- **Core definitions**: `core/voice.md`, `core/scoring.md`
- **Examples**: `examples/{number}-success-01.md`, `{number}-failure-01.md`
- English examples: `examples/en-{number}-success-01.md`
@@ -39,21 +39,19 @@ The project uses a **plugin architecture**: patterns are plugins (`patterns/{lan
6. Update `README.md` pattern tables
7. Bump the pack's `version:` frontmatter
-## When Adding a Profile
-
-1. Copy `profiles/default.md` as a template
-2. Define `voice-overrides` (amplify/allow/suppress per voice dimension)
-3. Define `pattern-overrides` (amplify/normal/reduce/suppress per pattern number)
- - Use language-scoped overrides to avoid cross-language number collisions:
- ```yaml
- pattern-overrides:
- ko:
- 8: amplify
- en:
- 8: amplify
- ```
-4. If the profile needs custom AI/fidelity balance, add to `.patina.default.yaml` under `scoring.combined-weights`
-5. Update `README.md` profile table
+## When Adding a Document Type
+
+1. Copy `document-types/default.md` as a template.
+2. Set `document-type:` to the filename stem.
+3. Define `purpose`, `audience`, `structure`, `style`, `avoid`, and
+ language-scoped `pattern-overrides` in frontmatter.
+4. Keep Persona voice, Register, and verification fields out of the policy.
+5. Add a matching key under `.patina.default.yaml`
+ `scoring.combined-weights.document-type` only when scoring evidence justifies
+ a non-default balance.
+
+Create reusable voice through Persona v2 (`patina persona new`). Persona schema
+validation rejects Document Type, Register, pattern-policy, and safety fields.
## When Modifying SKILL.md
@@ -68,14 +66,13 @@ The project uses a **plugin architecture**: patterns are plugins (`patterns/{lan
## Version Management
-- `.patina.default.yaml` `version:` is the **source of truth**
-- `SKILL.md` and `README.md` must match it
-- Pattern pack versions are independent — bump when patterns change
-- Profile versions are independent
+- `package.json` `version` is the **source of truth**
+- `SKILL.md`, `.patina.default.yaml`, and `README.md` must match it
+- Pattern pack and Document Type versions are independent
## Language Synchronization Rule
-All 4 languages (ko, en, zh, ja) must maintain **29 patterns each** (116 total). When adding a universal pattern:
+All 4 languages (ko, en, zh, ja) currently maintain **46 patterns each** (184 total). When adding a universal pattern:
- Same pattern number across all 4 languages
- Same semantic category
- Language-specific watch words and examples
diff --git a/.patina.default.yaml b/.patina.default.yaml
index b746395..a6c9ec0 100644
--- a/.patina.default.yaml
+++ b/.patina.default.yaml
@@ -4,41 +4,26 @@ language: ko # Korean (default) -- auto-loads all ko-*.md patterns
# language: zh -- Chinese -- auto-loads all zh-*.md patterns
# language: ja -- Japanese -- auto-loads all ja-*.md patterns
# Override with CLI: --lang en / --lang zh / --lang ja
-profile: default
-output: rewrite # rewrite | diff | audit | score
+document-type: default # pattern policy: default, blog, academic, technical,
+ # formal, social, email, legal, medical, marketing,
+ # narrative, instructional, casual-conversation,
+ # code-comment, commit-message, release-notes, namuwiki
+# `formal` means structured documents; `casual-conversation` means chat/message
+# structure. Neither selects casual/professional delivery — use `register`.
+output: rewrite # rewrite | diff | audit | score
-
-# Tone categorization (v3.10): register axis (casual/professional) for ko/en.
-# Resolution: explicit --tone > config tone > config profile.
-# auto = heuristic single-tone detection at SKILL.md Phase 4.5b.
-# Unset = profile-only legacy behavior (regression-safe default).
-tone: # one of: casual, professional, auto (register only)
- # Override with CLI: --tone
- # zh/ja with explicit tone: warns and falls back to profile.
-
-# Persona harness (v1): Korean rewrite defaults to the conservative preserve
-# persona. Non-rewrite and non-Korean surfaces do not apply persona implicitly.
-persona: preserve
+# Optional rewrite axes. Omit persona to preserve source voice; omit register
+# to preserve the source register.
+register: # casual | professional
+# --persona applies a reusable voice fingerprint from personas/{lang}/.
+persona:
personas:
- library: personas/ko
thresholds:
- schema: patina.persona.thresholds.v1
- # source stays `placeholder` because the advisory tuning below is informed by
- # observation, not a formal 2-round promotion. The ENFORCING floors, however,
- # are the product's core safety contract and do not depend on calibration.
+ # Persona match and surface churn are advisory voice-quality signals.
+ # Meaning, numbers, and fidelity are enforced by the global rewrite and
+ # --verify paths whether or not a persona is active.
source: placeholder
- calibration_rounds_required: 2
- # --- ENFORCING (safety gate; a miss sets a non-zero exit, non-destructive) ---
- # Meaning + facts only. Enforced when evaluated (mps/fidelity need --verify or a
- # backend-reported score); dropped source numbers always enforce deterministically.
- mps_floor: 70
- fidelity_floor: 70
- # --- ADVISORY (warn only; never blocks or changes the exit code) ---
- # persona_match: voice-quality signal. Observed real KO persona-match runs ~35-56,
- # so 45 flags the low end without firing on every rewrite.
persona_match_min: 45
- # churn: surface token change, NOT a meaning signal. Live KO humanizing rewrites
- # churn ~0.5-0.85 while preserving meaning, so 0.9 flags only near-total rewrites.
churn_max: 0.9
# Supported languages: ko, en, zh, ja
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 091c7d4..b894c42 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,10 @@ Semver rationale: major — removes public configuration keys and the obsolete a
### Added
- `patina --score --offline` runs the deterministic scoring layer without resolving a backend or credential. It supports markdown/JSON output and `--exit-on`; LLM-judged categories are marked unavailable, and a missing numeric local score fails instead of silently passing.
+- Three independent rewrite axes replace the overloaded profile/tone/formality model: 17 Document Types own document policy, 12 language-scoped Persona v2 files optionally supply reusable voice, and Register independently selects `casual` or `professional` delivery. Omitted Persona and Register preserve the source.
+- Document Type files now use a validated structured frontmatter contract (`purpose`, `audience`, `structure`, `style`, `avoid`, language-scoped `pattern-overrides`). Invalid or cross-axis custom policies fail before prompt assembly.
+- Rewrite prompts state axis ownership and precedence explicitly. Document Type cannot manufacture voice, Persona cannot choose document policy or Register, and Register cannot alter structure or claims.
+- The example catalog now includes comparable KO/EN rewrite-axis fixtures for academic, instructional, marketing, narrative, casual, and professional behavior.
### Fixed
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a2ad108..fa1af66 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,10 +1,10 @@
# Contributing to Patina
-Thanks for considering a contribution. Patina is a pattern-based tool, so the most impactful contributions are usually new patterns, better examples, or profile refinements.
+Thanks for considering a contribution. Patina is a pattern-based tool, so the most impactful contributions are usually new patterns, better examples, Document Type policies, or Persona refinements.
## Public vs. Internal Docs
-User-facing documentation lives in `README*.md`, `docs/`, `examples/`, `patterns/`, `profiles/`, and the skill entrypoints. Maintainer or agent notes (backlogs, runbooks, launch playbooks) are kept out of this repository entirely — they live in the maintainer's private workspace, and `docs/internal/` is gitignored as a guard. Only real user-facing facts get promoted into the public docs list.
+User-facing documentation lives in `README*.md`, `docs/`, `examples/`, `patterns/`, `document-types/`, `personas/`, and the skill entrypoints. Maintainer or agent notes (backlogs, runbooks, launch playbooks) are kept out of this repository entirely — they live in the maintainer's private workspace, and `docs/internal/` is gitignored as a guard. Only real user-facing facts get promoted into the public docs list.
When moving a root-level Markdown file, link it from `README.md` if it is public; if it is maintainer-internal, it does not belong in the repository.
@@ -73,7 +73,7 @@ False positives are expected, especially for academic, encyclopedic, legal, corp
1. Use the false-positive issue template.
2. Include language, genre/register, score/audit excerpt, and the specific pattern that over-fired.
3. Remove private text or replace it with a minimal redistributable excerpt.
-4. Suggest whether the fix should be an exclusion rule, lower severity, profile override, or benchmark fixture.
+4. Suggest whether the fix should be an exclusion rule, lower severity, Document Type `pattern-overrides` change, or benchmark fixture.
Maintainers should prefer tightening exclusions over deleting patterns outright.
@@ -160,11 +160,18 @@ See [docs/HARNESS.md](docs/HARNESS.md) for the full measurement-tool map.
- Add a target-language false-positive note when a phrase is normal in that register.
- Keep examples redistributable; do not paste private user text.
-## Adding a Profile
+## Adding a Document Type
-Profiles live in `profiles/{name}.md`. Copy an existing one (e.g. `blog.md`) and adjust:
-- `voice-overrides`: which voice dimensions to amplify/suppress
-- `pattern-overrides`: per-language, per-pattern-id actions. `suppress` is applied deterministically (the pattern is dropped from the prompt for that language); `reduce`/`amplify` are advisory and reinforced only by the prose body.
+Document Types live in `document-types/{name}.md`. Copy an existing policy
+(for example `blog.md`), set `document-type:` to the filename stem, and define
+`scope`, `purpose`, `audience`, `structure`, `style`, `avoid`, and
+language-scoped `pattern-overrides`. `suppress` is applied deterministically;
+`reduce`/`amplify` document policy intent but do not currently change a runtime
+weight. Keep Persona voice, casual/professional Register markers, and
+verification thresholds out of this axis.
+
+For reusable voice, create a Persona v2 with `patina persona new`. Persona
+frontmatter is validated to reject document policy, Register, and safety fields.
## Pattern Staleness
@@ -176,14 +183,14 @@ How we handle this:
- **Quarterly review:** Maintainers follow [`process/pattern-freshness.md`](process/pattern-freshness.md) for corpus freeze windows, promotion thresholds, and frontmatter metadata
- **Lexicon provenance:** Newly mined or re-mined lexicon entries need `added`, `source`, and `last_validated` provenance before changing shipped behavior; run `npm run lexicon:freshness` to verify sidecars match the shipped entries
- **Version notes:** Each pattern pack has a `version` field — bump it when patterns change
-- **No deletion without replacement:** We don't remove patterns outright; we mark them as `low` severity or move them to `reduce` in profiles
+- **No deletion without replacement:** We don't remove patterns outright; we mark them as `low` severity or move them to `reduce` in a Document Type policy
## Versioning Policy
Patina uses semantic versioning for both CLI behavior and pattern-pack compatibility.
- **Major:** remove or renumber patterns, break config/result schemas, change public CLI semantics, or make existing pattern packs incompatible.
-- **Minor:** add a pattern, language, profile, mode, backend, benchmark schema field, or contributor-facing workflow.
+- **Minor:** add a pattern, language, Document Type, Persona, mode, backend, benchmark schema field, or contributor-facing workflow.
- **Patch:** fix bugs, adjust severity/exclusions, clarify examples, update docs, refresh benchmark fixtures without changing schemas.
Every changelog entry should include a short semver rationale line so downstream users know whether to pin, test, or upgrade normally.
diff --git a/CONTRIBUTING_KR.md b/CONTRIBUTING_KR.md
index 3b5fb74..91a64d7 100644
--- a/CONTRIBUTING_KR.md
+++ b/CONTRIBUTING_KR.md
@@ -1,10 +1,10 @@
# Patina에 기여하기
-기여를 검토해 주셔서 감사합니다. Patina는 패턴 기반 도구이므로 가장 큰 도움이 되는 기여는 새 패턴, 더 나은 예시, 프로필 개선인 경우가 많습니다.
+기여를 검토해 주셔서 감사합니다. Patina는 패턴 기반 도구이므로 가장 큰 도움이 되는 기여는 새 패턴, 더 나은 예시, Document Type 정책, Persona 개선인 경우가 많습니다.
## 공개 문서와 내부 문서
-사용자용 문서는 `README*.md`, `docs/`, `examples/`, `patterns/`, `profiles/`, 스킬 엔트리포인트에 둡니다. 유지보수자나 에이전트용 메모(백로그, 런북, 런치 플레이북)는 이 저장소에 두지 않습니다 — 유지보수자의 비공개 작업 공간에 보관하며, `docs/internal/`은 안전장치로 gitignore 처리되어 있습니다. 실제 사용자에게 필요한 사실만 공개 문서 목록으로 승격합니다.
+사용자용 문서는 `README*.md`, `docs/`, `examples/`, `patterns/`, `document-types/`, `personas/`, 스킬 엔트리포인트에 둡니다. 유지보수자나 에이전트용 메모(백로그, 런북, 런치 플레이북)는 이 저장소에 두지 않습니다 — 유지보수자의 비공개 작업 공간에 보관하며, `docs/internal/`은 안전장치로 gitignore 처리되어 있습니다. 실제 사용자에게 필요한 사실만 공개 문서 목록으로 승격합니다.
루트의 Markdown 파일을 옮길 때 공개 문서라면 `README.md`에서 링크하고, 유지보수자 내부 문서라면 저장소 밖으로 옮깁니다.
@@ -73,7 +73,7 @@ PR에서 위 영어 문서를 바꾸면 같은 PR에서 한국어 쌍도 갱신
1. false-positive 이슈 템플릿을 사용합니다.
2. 언어, 장르/문체, score/audit excerpt, 과하게 감지된 패턴을 포함합니다.
3. 비공개 텍스트를 제거하거나 재배포 가능한 최소 발췌로 바꿉니다.
-4. 수정 방향이 exclusion rule, 낮은 severity, profile override, benchmark fixture 중 무엇인지 제안합니다.
+4. 수정 방향이 exclusion rule, 낮은 severity, Document Type `pattern-overrides` 변경, benchmark fixture 중 무엇인지 제안합니다.
유지보수자는 패턴을 바로 삭제하기보다 exclusion을 좁히는 쪽을 우선해야 합니다.
@@ -126,11 +126,17 @@ npm run benchmark:report
- 어떤 표현이 해당 문체에서는 정상이라면 대상 언어의 오탐 메모를 추가합니다.
- 예시는 재배포 가능해야 합니다. 비공개 사용자 텍스트를 붙여 넣지 마세요.
-## 프로필 추가
+## Document Type 추가
-프로필은 `profiles/{name}.md`에 있습니다. 기존 파일(예: `blog.md`)을 복사한 뒤 조정합니다.
-- `voice-overrides`: 어떤 voice dimension을 키우거나 줄일지
-- `pattern-overrides`: 언어별 패턴 severity 조정
+Document Type은 `document-types/{name}.md`에 있습니다. 기존 정책(예:
+`blog.md`)을 복사하고 `document-type:`을 파일명 stem과 맞춘 뒤 `scope`,
+`purpose`, `audience`, `structure`, `style`, `avoid`, 언어별
+`pattern-overrides`를 정의합니다. `suppress`는 결정론적으로 적용되며
+`reduce`/`amplify`는 현재 정책 의도만 기록합니다. Persona 목소리,
+casual/professional Register 표지, 검증 하한은 이 축에 넣지 않습니다.
+
+재사용 voice는 `patina persona new`로 Persona v2를 만드세요. schema는
+document policy, Register, safety field가 Persona에 들어오면 거부합니다.
## 패턴 노후화
@@ -142,7 +148,7 @@ AI 문체 패턴은 모델이 미세 조정되면서 바뀝니다. 어떤 패턴
- **분기별 리뷰:** 유지보수자는 [`process/pattern-freshness.md`](process/pattern-freshness.md)의 corpus freeze window, promotion threshold, frontmatter metadata 규칙을 따릅니다.
- **Lexicon provenance:** 새로 마이닝하거나 다시 마이닝한 lexicon 항목은 동작을 바꾸기 전에 `added`, `source`, `last_validated` provenance를 기록해야 하며, `npm run lexicon:freshness`로 sidecar가 실제 shipped entry와 맞는지 확인합니다.
- **버전 메모:** 각 패턴 팩에는 `version` 필드가 있습니다. 패턴이 바뀌면 올립니다.
-- **대체 없는 삭제 금지:** 패턴을 바로 제거하지 않습니다. `low` severity로 낮추거나 프로필에서 `reduce`로 옮깁니다.
+- **대체 없는 삭제 금지:** 패턴을 바로 제거하지 않습니다. `low` severity로 낮추거나 Document Type에서 `reduce`로 옮깁니다.
## 버전 정책
diff --git a/INSTALLATION.md b/INSTALLATION.md
index 3edb90c..565f9b9 100644
--- a/INSTALLATION.md
+++ b/INSTALLATION.md
@@ -172,10 +172,10 @@ The user can now invoke patina as a slash command in their agent:
[paste their text here]
```
-Or with a persona or tone (register):
+Or with a Persona and/or Register:
```
-/patina --tone professional
+/patina --persona natural-en --register professional
[paste their text]
```
@@ -190,7 +190,7 @@ Or through Docker after the GHCR release image exists:
```bash
printf '%s\n' 'Coffee has emerged as a pivotal cultural phenomenon.' \
- | docker run --rm -i -e PATINA_API_KEY ghcr.io/devswha/patina:6.2.0 --lang en --provider openai
+ | docker run --rm -i -e PATINA_API_KEY ghcr.io/devswha/patina:7.0.0 --lang en --provider openai
```
The Docker image intentionally does not bake in codex/claude/gemini CLI binaries or logins. Use API-backed providers inside the container, or mount your own authenticated tooling explicitly.
diff --git a/README.md b/README.md
index 2879149..73f1b2c 100644
--- a/README.md
+++ b/README.md
@@ -79,8 +79,8 @@ Then run the skill from Claude Code, Codex CLI, Cursor, or OpenCode:
Useful skill calls:
```text
-/patina --tone professional
-/patina --tone auto --lang en
+/patina --document-type email --register professional
+/patina --document-type blog --persona pragmatic-founder
```
### Standalone CLI
@@ -110,17 +110,41 @@ For large `--batch` runs, prefer an OpenAI-compatible HTTP backend; local CLI ba
| **184 patterns** | 37 rewrite-capable + 9 score-only viral-hook per language (46 each across KO/EN/ZH/JA) — see the full 184-pattern catalog in [PATTERNS.md](docs/PATTERNS.md) |
| **Modes** | rewrite · verify · audit · score · diff |
| **Surfaces** | agent skill · Node CLI · in-place preview · browser playground (rewrite + score) |
-| **Voice controls** | `--persona` is the reusable voice axis · `--tone` overrides register · `--profile` sets Node pattern policy; some skill/core paths still carry legacy profile voice guidance |
+| **Rewrite axes** | `--document-type` controls genre/policy · `--persona` controls reusable voice · `--register` controls casual/professional delivery |
| **Free usage** | logged-in `codex`, `claude`, or `gemini` CLI can run rewrites without `PATINA_API_KEY` |
| **Calibration** | 67.3% editing-hotspot catch [63.5–71.0%] across GPT-5.5 / Claude Sonnet 4.6 / Gemini 2.5 Pro (n=600, KO+EN); 16.0% false positives [11.6–21.7%] on KO+EN human controls (n=200) |
| **License** | MIT |
Scores are editing signals with false positives and false negatives, not proof of authorship. See [Ethics](docs/ETHICS.md).
+## Three Independent Axes
+
+Patina does not infer one axis from another. Omit Persona and Register to keep
+the source voice and register.
+
+| Axis | Controls | Does not control | CLI | Config | Playground |
+|---|---|---|---|---|---|
+| **Document Type** | Genre, purpose, structural conventions, pattern policy | Voice, casual/professional delivery, meaning floors | `--document-type` | `document-type` | Document |
+| **Persona** | Reusable voice fingerprint: vocabulary, rhythm, explanation habits | Genre, pattern policy, register, meaning floors | `--persona` | `persona` | Voice |
+| **Register** | `casual` or `professional` delivery | Genre, persona identity, pattern policy | `--register` | `register` | Register |
+Meaning preservation is the outer guard. If instructions appear to overlap,
+field ownership resolves them: Document Type wins for document structure and
+domain constraints, Persona for idiolect and rhythm, and Register for
+casual/professional markers. An explicit value never fills an omitted axis.
+
+
+Examples:
+
+```bash
+patina --document-type email --register professional note.md
+patina --document-type blog --persona pragmatic-founder post.md
+patina --document-type technical --persona technical-explainer --register casual guide.md
+```
+
## Common Commands
```bash
-patina --lang [mode] [--profile ] input.txt
+patina --lang [mode] [--document-type ] [--persona ] [--register ] input.txt
```
| Command | Purpose |
@@ -133,13 +157,13 @@ patina --lang [mode] [--profile ] input.txt
| `patina --diff input.txt` | show pattern-by-pattern changes |
| `patina --preview page.html` | render rewrites back onto a saved HTML page with toggles and inline diff |
| `patina --verify input.txt` | rewrite, then check MPS/fidelity floors with one retry |
-| `patina --tone auto --lang en input.txt` | infer and apply a KO/EN tone axis |
+| `patina --document-type email --register professional input.txt` | use email conventions with professional delivery |
| `patina --persona pragmatic-founder input.txt` | rewrite in a built-in voice persona |
| `patina persona new my-voice --from-sample past.txt` | author your own persona from a writing sample |
| `patina persona list` | list built-in + custom personas |
| `patina persona show my-voice --json` | print a persona's normalized config (never the docs body) |
| `patina persona edit my-voice --name "New Name"` | copy-on-edit a persona into `custom/personas/` |
-| `patina persona rm my-voice` | remove a custom persona (built-ins + `preserve` protected) |
+| `patina persona rm my-voice` | remove a custom persona (built-ins are protected) |
| `patina --format json --quiet input.txt` | script-friendly output |
| `patina --batch docs/*.md --outdir cleaned/` | batch file processing |
@@ -159,7 +183,7 @@ patina persona edit my-voice --name "Founder voice" # copy-on-edit into
patina persona rm my-voice # remove a custom persona (--force to skip the confirm)
```
-Works on ko/en/zh/ja and composes with `--tone` and `--profile`. In Node, persona owns reusable voice, register precedence is `--tone` > persona, and profile controls pattern policy. Some skill/core-prompt paths still contain legacy profile voice guidance, so that separation is not universal yet. None of these controls lowers the meaning floors: authored personas are validated on save, and rewrites still enforce MPS/fidelity and dropped-number checks.
+Works on ko/en/zh/ja and composes independently with `--document-type` and `--register`. Persona v2 changes reusable voice only; it cannot select a document type, set register, change pattern policy, or define verification/meaning floors. Authored Personas are validated on save, while global rewrite guards and `--verify` enforce meaning preservation independently.
## CI
@@ -204,12 +228,12 @@ If meaning drifts, the change is retried or rolled back. Deterministic analysis
## Configuration
```yaml
-# .patina.default.yaml
+# .patina.yaml
version: "7.0.0"
language: ko # ko | en | zh | ja
-profile: default
-output: rewrite # rewrite | diff | audit | score
-tone: # casual | professional | auto (register override; profile = pattern policy)
+document-type: default # genre/purpose + pattern policy
+persona: # optional reusable voice; omit to preserve source
+register: # casual | professional; omit to preserve source
```
Project `.patina.yaml` overrides defaults. Pattern packs are auto-discovered by language prefix. Additive list keys (`blocklist`, `allowlist`, `skip-patterns`) merge; other arrays replace.
diff --git a/README_JA.md b/README_JA.md
index 872f293..68feb13 100644
--- a/README_JA.md
+++ b/README_JA.md
@@ -68,8 +68,8 @@ curl -fsSL https://raw.githubusercontent.com/devswha/patina/main/install.sh | ba
便利なスキル呼び出し:
```text
-/patina --tone professional
-/patina --tone auto --lang en
+/patina --document-type email --register professional
+/patina --document-type blog --persona pragmatic-founder
```
### スタンドアロン CLI
@@ -99,17 +99,33 @@ printf '%s\n' 'Coffee has emerged as a pivotal cultural phenomenon.' \
| **184 パターン** | 各言語 37 個の書き換え可能パターン + 9 個のスコア専用 viral-hook(KO/EN/ZH/JA 各 46 個) — 完全な 184 パターンカタログは [PATTERNS.md](docs/PATTERNS.md) を参照 |
| **モード** | rewrite · verify · audit · score · diff |
| **利用形態** | agent skill · Node CLI · ページ内 preview · ブラウザ playground(rewrite + score) |
-| **ボイス制御** | `--persona` は再利用ボイス · `--tone` はレジスター上書き · `--profile` は Node のパターン方針(一部 skill/core 経路には旧 profile ボイス指示が残る) |
+| **3つの書き換え軸** | `--document-type` はジャンル/方針 · `--persona` は再利用ボイス · `--register` は casual/professional レジスター |
| **無料利用** | ログイン済みの `codex`、`claude`、`gemini` CLI なら `PATINA_API_KEY` なしで書き換え可能 |
| **キャリブレーション** | GPT-5.5 / Claude Sonnet 4.6 / Gemini 2.5 Pro で編集ホットスポット再現率 67.3% [63.5–71.0%](n=600、KO+EN);KO+EN の人間文章コントロールで誤検出 16.0% [11.6–21.7%](n=200) |
| **ライセンス** | MIT |
スコアは誤検出と見逃しを含む編集シグナルであり、著者判定の根拠ではありません。[Ethics](docs/ETHICS.md) を参照してください。
+## 互いに独立した3つの軸
+
+patina は一つの軸から別の軸を推論しません。Persona と Register を省略すると、原文のボイスとレジスターを保持します。
+
+| 軸 | 制御するもの | 制御しないもの | CLI | 設定 | Playground |
+|---|---|---|---|---|---|
+| **Document Type** | ジャンル、目的、構造慣習、パターン方針 | ボイス、casual/professional の伝え方、意味保全フロア | `--document-type` | `document-type` | Document |
+| **Persona** | 再利用ボイス指紋:語彙、リズム、説明習慣 | ジャンル、パターン方針、Register、意味保全フロア | `--persona` | `persona` | Voice |
+| **Register** | `casual` または `professional` の伝え方 | ジャンル、Persona の同一性、パターン方針 | `--register` | `register` | Register |
+
+```bash
+patina --document-type email --register professional note.md
+patina --document-type blog --persona pragmatic-founder post.md
+patina --document-type technical --persona technical-explainer --register casual guide.md
+```
+
## 主なコマンド
```bash
-patina --lang [mode] [--profile ] input.txt
+patina --lang [mode] [--document-type ] [--persona ] [--register ] input.txt
```
| コマンド | 目的 |
@@ -122,10 +138,13 @@ patina --lang [mode] [--profile ] input.txt
| `patina --diff input.txt` | 変更をパターンごとに表示 |
| `patina --preview page.html` | 保存済み HTML ページ上に書き換えを反映し、トグルとインライン diff を表示 |
| `patina --verify input.txt` | 書き換え後、1 回のリトライで MPS/忠実度フロアを検査 |
-| `patina --tone auto --lang en input.txt` | KO/EN のトーン軸を推定して適用 |
+| `patina --document-type email --register professional input.txt` | メールの慣習と professional レジスターを適用 |
| `patina --persona pragmatic-founder input.txt` | 組み込みボイスペルソナで書き換え |
| `patina persona new my-voice --from-sample past.txt` | 文章サンプルから自分のペルソナを作成 |
| `patina persona list` | 組み込み + カスタムペルソナを一覧表示 |
+| `patina persona show my-voice --json` | Persona の正規化済みボイス設定を表示 |
+| `patina persona edit my-voice --name "New Name"` | 組み込み Persona を custom shadow にコピーして編集 |
+| `patina persona rm my-voice` | カスタム Persona を削除(組み込みは保護) |
| `patina --format json --quiet input.txt` | スクリプト向け出力 |
| `patina --batch docs/*.md --outdir cleaned/` | 複数ファイルの一括処理 |
@@ -141,7 +160,7 @@ patina persona new my-voice --describe "plain-spoken founder, casual"
patina --persona my-voice draft.md # then reuse it
```
-ko/en/zh/ja で `--tone`/`--profile` と組み合わせられます。Node では persona が再利用ボイスを担い、レジスター優先順位は `--tone` > persona、profile はパターン方針を制御します。一部の skill/core-prompt 経路には旧 profile ボイス指示が残るため、この分離はまだ全経路の不変条件ではありません。どの制御も意味フロアを下げず、作成した persona は保存時に検証され、書き換えは MPS/忠実度と数値欠落チェックを引き続き強制します。
+ko/en/zh/ja で `--document-type`、`--persona`、`--register` を独立して組み合わせられます。Persona v2 は再利用ボイスだけを変え、文書タイプ、レジスター、パターン方針、verification、意味フロアを定義できません。作成した Persona は保存時に検証され、グローバル rewrite guard と `--verify` が意味保全を独立して担当します。
## CI
@@ -189,9 +208,9 @@ Input
# .patina.default.yaml
version: "7.0.0"
language: ko # ko | en | zh | ja
-profile: default
-output: rewrite # rewrite | diff | audit | score
-tone: # casual | professional | auto (register override; profile = pattern policy)
+document-type: default # ジャンル/用途 + パターン方針
+persona: # 任意。省略時は原文ボイスを保持
+register: # casual | professional。省略時は原文レジスターを保持
```
プロジェクトの `.patina.yaml` がデフォルトを上書きします。パターンパックは言語プレフィックスで自動検出されます。追加型のリストキー(`blocklist`、`allowlist`、`skip-patterns`)はマージされ、その他の配列は置き換えられます。
diff --git a/README_KR.md b/README_KR.md
index 1623dd7..a167e0c 100644
--- a/README_KR.md
+++ b/README_KR.md
@@ -68,8 +68,8 @@ curl -fsSL https://raw.githubusercontent.com/devswha/patina/main/install.sh | ba
유용한 스킬 호출:
```text
-/patina --tone professional
-/patina --tone auto --lang en
+/patina --document-type email --register professional
+/patina --document-type blog --persona pragmatic-founder
```
### 독립형 CLI
@@ -99,17 +99,38 @@ printf '%s\n' 'Coffee has emerged as a pivotal cultural phenomenon.' \
| **184개 패턴** | 언어별 재작성 가능 37개 + 스코어 전용 바이럴 훅 9개(KO/EN/ZH/JA 각각 46개) — 전체 184개 패턴 카탈로그는 [PATTERNS.md](docs/PATTERNS.md) 참고 |
| **모드** | rewrite · verify · audit · score · diff |
| **사용 채널** | 에이전트 스킬 · Node CLI · 페이지 내 preview · 브라우저 playground (리라이트 + 점수) |
-| **보이스 제어** | `--persona`는 재사용 보이스 · `--tone`은 register override · `--profile`은 Node 패턴 정책(일부 skill/core 경로에는 기존 profile voice guidance가 남아 있음) |
+| **재작성 3축** | `--document-type`은 장르/정책 · `--persona`는 재사용 보이스 · `--register`는 casual/professional 전달 방식 |
| **무료 사용** | 로그인된 `codex`, `claude`, `gemini` CLI 중 하나로 `PATINA_API_KEY` 없이 재작성 실행 |
| **캘리브레이션** | GPT-5.5 / Claude Sonnet 4.6 / Gemini 2.5 Pro 기준 편집 핫스팟 catch 67.3% [63.5–71.0%] (n=600, KO+EN); KO+EN 사람 글 컨트롤에서 오탐 16.0% [11.6–21.7%] (n=200) |
| **라이선스** | MIT |
점수는 오탐과 미탐이 있는 편집 신호이지 작성자 판정의 근거가 아닙니다. [Ethics](docs/ETHICS.md)를 참고하세요.
+## 서로 독립적인 세 축
+
+patina는 한 축에서 다른 축을 추론하지 않습니다. Persona와 Register를 생략하면 원문의 목소리와 레지스터를 보존합니다.
+
+| 축 | 정하는 것 | 정하지 않는 것 | CLI | 설정 | Playground |
+|---|---|---|---|---|---|
+| **Document Type** | 장르·용도·구조 관습·패턴 정책 | 목소리, casual/professional 전달 방식, 의미 보존 하한 | `--document-type` | `document-type` | Document |
+| **Persona** | 재사용 보이스 지문: 어휘·리듬·설명 습관 | 장르, 패턴 정책, Register, 의미 보존 하한 | `--persona` | `persona` | Voice |
+| **Register** | `casual` 또는 `professional` 전달 방식 | 장르, Persona 정체성, 패턴 정책 | `--register` | `register` | Register |
+의미 보존은 세 축 바깥의 공통 하한입니다. 지시가 겹쳐 보이면 소유 필드로
+판단합니다. 문서 구조와 도메인 제약은 Document Type, 고유 어휘와 리듬은 Persona,
+casual/professional 표지는 Register가 정합니다. 명시된 한 축으로 생략된 다른 축을
+추론하지 않습니다.
+
+
+```bash
+patina --document-type email --register professional note.md
+patina --document-type blog --persona pragmatic-founder post.md
+patina --document-type technical --persona technical-explainer --register casual guide.md
+```
+
## 자주 쓰는 명령
```bash
-patina --lang [mode] [--profile ] input.txt
+patina --lang [mode] [--document-type ] [--persona ] [--register ] input.txt
```
| 명령 | 목적 |
@@ -122,10 +143,13 @@ patina --lang [mode] [--profile ] input.txt
| `patina --diff input.txt` | 패턴별 변경 사항 표시 |
| `patina --preview page.html` | 저장된 HTML 페이지 위에 재작성을 다시 렌더링(토글 + 인라인 diff) |
| `patina --verify input.txt` | 재작성 후 MPS/충실도 하한을 검사하고 1회 재시도 |
-| `patina --tone auto --lang en input.txt` | KO/EN 톤 축을 추론해 적용 |
+| `patina --document-type email --register professional input.txt` | 이메일 관습과 professional 레지스터 적용 |
| `patina --persona pragmatic-founder input.txt` | 내장 보이스 페르소나로 재작성 |
| `patina persona new my-voice --from-sample past.txt` | 글 샘플에서 나만의 페르소나 제작 |
| `patina persona list` | 내장 + 커스텀 페르소나 목록 |
+| `patina persona show my-voice --json` | persona의 정규화된 voice config 조회 |
+| `patina persona edit my-voice --name "New Name"` | built-in은 custom shadow로 복사해 수정 |
+| `patina persona rm my-voice` | custom persona 삭제(built-in은 보호) |
| `patina --format json --quiet input.txt` | 스크립트 친화적 출력 |
| `patina --batch docs/*.md --outdir cleaned/` | 배치 파일 처리 |
@@ -141,7 +165,7 @@ patina persona new my-voice --describe "plain-spoken founder, casual"
patina --persona my-voice draft.md # 이후 재사용
```
-ko/en/zh/ja에서 `--tone`/`--profile`과 조합됩니다. Node에서는 persona가 재사용 보이스를 맡고 register 우선순위는 `--tone` > persona이며, profile은 패턴 정책을 제어합니다. 일부 skill/core-prompt 경로에는 기존 profile voice guidance가 남아 있어 이 구분이 아직 모든 경로의 불변식은 아닙니다. 어떤 제어도 의미 하한을 낮추지 않으며, 제작한 persona는 저장 시 검증되고 재작성은 MPS/충실도 및 숫자 누락 검사를 그대로 강제합니다.
+ko/en/zh/ja에서 `--document-type`/`--register`와 독립적으로 조합됩니다. Persona v2는 재사용 보이스만 바꾸며 문서 유형, 레지스터, 패턴 정책, verification/의미 하한을 정의할 수 없습니다. 제작한 Persona는 저장 시 검증되고, 전역 rewrite guard와 `--verify`가 의미 보존을 독립적으로 담당합니다.
## CI
@@ -189,9 +213,9 @@ Input
# .patina.default.yaml
version: "7.0.0"
language: ko # ko | en | zh | ja
-profile: default
-output: rewrite # rewrite | diff | audit | score
-tone: # casual | professional | auto (register override; profile = pattern policy)
+document-type: default # 장르·용도 + 패턴 정책
+persona: # 선택 사항; 생략하면 원문 보이스 보존
+register: # casual | professional; 생략하면 원문 레지스터 보존
```
프로젝트의 `.patina.yaml`이 기본값을 오버라이드합니다. 패턴 팩은 언어 접두사로 자동 탐색됩니다. 추가형 목록 키(`blocklist`, `allowlist`, `skip-patterns`)는 병합되고, 다른 배열은 대체됩니다.
diff --git a/README_ZH.md b/README_ZH.md
index 3045338..09e7b0c 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -70,8 +70,8 @@ curl -fsSL https://raw.githubusercontent.com/devswha/patina/main/install.sh | ba
常用 skill 调用:
```text
-/patina --tone professional
-/patina --tone auto --lang en
+/patina --document-type email --register professional
+/patina --document-type blog --persona pragmatic-founder
```
### 独立 CLI
@@ -101,17 +101,33 @@ printf '%s\n' 'Coffee has emerged as a pivotal cultural phenomenon.' \
| **184 条模式** | 每种语言 37 条可改写模式 + 9 条仅评分的病毒式钩子模式(KO/EN/ZH/JA 各 46 条)—— 完整的 184 条模式目录见 [PATTERNS.md](docs/PATTERNS.md) |
| **模式** | rewrite · verify · audit · score · diff |
| **使用入口** | agent skill · Node CLI · 页面内 preview · 浏览器 playground(改写 + 评分) |
-| **声音控制** | `--persona` 是可复用声音 · `--tone` 覆盖语域 · `--profile` 设置 Node 模式策略(部分 skill/core 路径仍有旧的 profile 声音指导) |
+| **三个改写轴** | `--document-type` 控制体裁/策略 · `--persona` 控制可复用声音 · `--register` 控制 casual/professional 语域 |
| **免费使用** | 已登录的 `codex`、`claude` 或 `gemini` CLI 可直接运行改写,无需 `PATINA_API_KEY` |
| **校准** | 编辑热点命中率 67.3% [63.5–71.0%],跨 GPT-5.5 / Claude Sonnet 4.6 / Gemini 2.5 Pro(n=600,KO+EN);在 KO+EN 人类对照上误检率 16.0% [11.6–21.7%](n=200) |
| **许可证** | MIT |
分数是有误报和漏报的编辑信号,不是作者身份的证明。见 [Ethics](docs/ETHICS.md)。
+## 三个相互独立的轴
+
+patina 不会从一个轴推断另一个轴。省略 Persona 和 Register 时,会保留原文的声音与语域。
+
+| 轴 | 控制 | 不控制 | CLI | 配置 | Playground |
+|---|---|---|---|---|---|
+| **Document Type** | 体裁、用途、结构惯例、模式策略 | 声音、casual/professional 表达、含义保留下限 | `--document-type` | `document-type` | Document |
+| **Persona** | 可复用声音指纹:词汇、节奏、解释习惯 | 体裁、模式策略、Register、含义保留下限 | `--persona` | `persona` | Voice |
+| **Register** | `casual` 或 `professional` 表达方式 | 体裁、Persona 身份、模式策略 | `--register` | `register` | Register |
+
+```bash
+patina --document-type email --register professional note.md
+patina --document-type blog --persona pragmatic-founder post.md
+patina --document-type technical --persona technical-explainer --register casual guide.md
+```
+
## 常用命令
```bash
-patina --lang [mode] [--profile ] input.txt
+patina --lang [mode] [--document-type ] [--persona ] [--register ] input.txt
```
| 命令 | 用途 |
@@ -124,10 +140,13 @@ patina --lang [mode] [--profile ] input.txt
| `patina --diff input.txt` | 按模式逐项展示改动 |
| `patina --preview page.html` | 把改写结果渲染回保存的 HTML 页面,带视图切换和内联 diff |
| `patina --verify input.txt` | 改写后检查 MPS/忠实度下限,并重试一次 |
-| `patina --tone auto --lang en input.txt` | 推断并应用 KO/EN 语气轴 |
+| `patina --document-type email --register professional input.txt` | 使用邮件惯例和 professional 语域 |
| `patina --persona pragmatic-founder input.txt` | 用内置声音人格改写 |
| `patina persona new my-voice --from-sample past.txt` | 从写作样本创建你自己的人格 |
| `patina persona list` | 列出内置 + 自制人格 |
+| `patina persona show my-voice --json` | 查看人格的规范化声音配置 |
+| `patina persona edit my-voice --name "New Name"` | 将内置人格复制为 custom shadow 后编辑 |
+| `patina persona rm my-voice` | 删除自制人格(内置人格受保护) |
| `patina --format json --quiet input.txt` | 适合脚本的输出 |
| `patina --batch docs/*.md --outdir cleaned/` | 批量文件处理 |
@@ -143,7 +162,7 @@ patina persona new my-voice --describe "plain-spoken founder, casual"
patina --persona my-voice draft.md # 之后复用
```
-可在 ko/en/zh/ja 上与 `--tone`/`--profile` 组合。Node 中,persona 负责可复用声音,语域优先级是 `--tone` > persona,profile 负责模式策略。部分 skill/core-prompt 路径仍保留旧的 profile 声音指导,因此这一区分尚未在所有路径中完全实现。任何控制都不会降低含义下限:自制 persona 会在保存时校验,改写仍强制执行 MPS/忠实度和数字缺失检查。
+可在 ko/en/zh/ja 上独立组合 `--document-type`、`--persona` 与 `--register`。Persona v2 只改变可复用声音,不能定义文档类型、语域、模式策略、verification 或含义下限。自制 Persona 会在保存时校验;全局 rewrite guard 和 `--verify` 独立负责含义保留。
## CI
@@ -191,9 +210,9 @@ Input
# .patina.default.yaml
version: "7.0.0"
language: ko # ko | en | zh | ja
-profile: default
-output: rewrite # rewrite | diff | audit | score
-tone: # casual | professional | auto (register override; profile = pattern policy)
+document-type: default # 体裁/用途 + 模式策略
+persona: # 可选;省略时保留原文声音
+register: # casual | professional;省略时保留原文语域
```
项目级 `.patina.yaml` 会覆盖默认值。模式包按语言前缀自动发现。可追加的列表键(`blocklist`、`allowlist`、`skip-patterns`)会合并;其他数组会直接替换。
diff --git a/SKILL.md b/SKILL.md
index b7429dc..f0beca6 100644
--- a/SKILL.md
+++ b/SKILL.md
@@ -33,7 +33,9 @@ Glob ./.patina.yaml → 있으면 Read
우선순위는 `.patina.default.yaml` → `~/.patina.yaml` → `./.patina.yaml` → `$ARGUMENTS`다. YAML 매핑은 재귀 병합하고 `blocklist`, `allowlist`, `skip-patterns`는 중복 없이 합친다. 다른 배열은 뒤 설정의 값으로 교체한다.
설정에서 다음을 확인:
-- `profile`: 사용할 프로필 (기본: `default`)
+- `document-type`: 문서 장르·용도와 패턴 정책 (기본: `default`)
+- `persona`: 선택적 재사용 목소리. 생략하면 원문 목소리 보존
+- `register`: 선택적 격식 수준 (`casual | professional`). 생략하면 원문 레지스터 보존
- `patterns`: 로드할 패턴 팩 목록
- `skip-patterns`: 건너뛸 패턴 팩
- `output`: 출력 모드 (`rewrite` | `diff` | `audit` | `score`)
@@ -41,41 +43,32 @@ Glob ./.patina.yaml → 있으면 Read
- `allowlist`: 감지 제외 어휘
`$ARGUMENTS`에서 옵션을 파싱하여 설정을 오버라이드:
-- `--profile `: 프로필 변경
+- `--document-type `: 문서 유형 변경
+- `--persona `: 재사용 목소리 선택
+- `--register `: 격식 수준 변경. `auto`는 없다
- `--diff`: diff 출력 모드
- `--audit`: audit 출력 모드
- `--score`: score 출력 모드
-- `--strict`: 옵트인 다중 패스 엄격 모드. rewrite 출력 모드를 사용한다. `--lang`, `--tone`, `--profile`와 함께 사용할 수 있다. `--audit`, `--diff`, `--score`와 함께 사용할 수 없다.
-- `--lang `: 처리 언어 변경 (ko, en, zh, ja). 설정 파일의 `language` 값을 오버라이드한다.
-- `--tone `: 어투(레지스터) 지정. 유효값: `casual | professional | auto`. academic/marketing/narrative/instructional 은 장르이므로 `--profile ` 을 쓴다. 알 수 없는 값이면 즉시 오류: "Unknown tone ''. Valid tones: casual, professional, auto"
-- `--batch `: 여러 파일을 한꺼번에 처리 (glob 또는 명시적 경로 목록).
- - `--in-place`: 원본 파일을 교정된 텍스트로 덮어쓴다.
- - `--suffix `: 결과를 `{원본명}{ext}` 파일로 저장한다 (예: `--suffix .humanized`).
- - `--outdir `: 결과를 지정 디렉토리에 저장한다.
+- `--strict`: 옵트인 다중 패스 엄격 모드. rewrite 출력 모드를 사용하며 세 축과 조합할 수 있다. `--audit`, `--diff`, `--score`와는 함께 쓸 수 없다
+- `--lang `: 처리 언어 변경 (`ko | en | zh | ja`)
+- `--batch `: 여러 파일을 한꺼번에 처리 (glob 또는 명시적 경로 목록)
+ - `--in-place`: 원본 파일을 교정된 텍스트로 덮어쓴다
+ - `--suffix `: 결과를 `{원본명}{ext}` 파일로 저장한다
+ - `--outdir `: 결과를 지정 디렉토리에 저장한다
-**톤 해석 우선순위 (v3.10):**
+세 축은 서로를 추론하거나 선택하지 않는다:
```
-resolved_tone = CLI --tone || config.tone || null
-
-if resolved_tone == null and config.profile present:
- → "profile-only mode" (4.5b 단계 건너뜀; footer: tone=null, tone_source=profile_only)
-
-if resolved_tone == "auto":
- → 4.5b 단계에서 휴리스틱 자동 감지 수행 (casual 또는 professional 로 확정)
-
-if resolved_tone in {casual, professional}:
- → tone_source=user, tone_evidence=["user-specified"], tone_confidence=high
- → 톤은 어투(레지스터)만 정한다. 장르 프로필은 `--profile` 로만 결정되며 톤은 프로필을 고르지 않는다 (백본 매핑 제거, 6.0).
- → academic / narrative / marketing / instructional 은 더 이상 톤이 아니다 — 그건 장르이므로 `--profile ` 을 쓴다.
-
-if --lang in {zh, ja} and resolved_tone != null:
- → footer에 경고 추가: tone "" is en/ko-only in v1; falling back to default profile
- → tone_source=unsupported_language_fallback
- → profile-only 경로로 fallback
+document_type = CLI --document-type || config.document-type || "default"
+persona = CLI --persona || config.persona || null
+register = CLI --register || config.register || null
```
-**legal/medical fidelity 보존 (R2):** resolved_tone=professional이더라도 config.profile이 `legal` 또는 `medical`이면 `scoring.combined-weights.legal/medical` (fidelity 0.65)을 강제 적용한다.
+- Document Type은 문서 관습과 `pattern-overrides`만 정한다. 목소리와 격식을 바꾸지 않는다.
+- Persona는 목소리 특성만 정한다. 문서 유형·레지스터·안전 하한을 바꾸지 않는다.
+- Register는 `casual | professional` 전달 방식만 정한다. 장르나 페르소나를 고르지 않는다.
+- 생략된 Persona와 Register는 각각 원문의 목소리와 레지스터를 보존한다.
+- `profile`, `tone`, `formality`, `--profile`, `--tone`, `--formality`는 v7 입력이 아니다. 사용되면 새 축 이름을 안내하고 실패한다.
`--score`이면 `core/scoring.md`도 로드한다.
파일이 없으면 에러: "core/scoring.md not found. Please update patina."
@@ -104,29 +97,29 @@ Glob custom/patterns/{lang}-*.md → Read (사용자 커스텀 패턴 추가 로
---
-## 3단계: 프로필 로드
+## 3단계: Document Type 로드
```
-Read profiles/{profile}.md
-Glob custom/profiles/{profile}.md → Read (사용자 커스텀 프로필 우선 로드)
+Glob custom/document-types/{document-type}.md → 있으면 Read
+Read document-types/{document-type}.md
```
-커스텀 프로필(`custom/profiles/{profile}.md`)이 있으면 우선 사용한다. 없으면 `profiles/{profile}.md`를 사용한다. 둘 다 없으면 `profiles/default.md`를 사용한다. `profiles/namuwiki.md`는 한국어 전용이므로 `--lang`이 `ko`가 아니면 기본 프로필로 폴백한다.
-
-프로필에 `voice-overrides`와 `pattern-overrides`가 있으면 이를 파싱하여 5단계에서 적용한다.
-- `voice-overrides`: voice.md 지침의 강도를 프로필별로 조절 (amplify/allow/suppress)
-- `pattern-overrides`: 특정 패턴의 교정 강도를 조절 (amplify/normal/reduce/suppress)
+커스텀 문서 유형이 있으면 우선 사용한다. 둘 다 없으면 `document-types/default.md`를 사용한다. `namuwiki`는 한국어 전용이므로 다른 언어에서는 `default`로 폴백한다.
-이 필드가 없는 프로필(예: default.md)은 기본값을 사용한다.
+문서 유형의 frontmatter에서 `purpose`, `audience`, `structure`, `style`, `avoid`, `pattern-overrides`를 읽어 4.8단계 문서 브리프와 5단계 패턴 처리에 적용한다. Markdown 본문은 문서로만 취급한다. Document Type은 장르·용도·구조 관습·패턴 정책만 정하며 Persona 목소리, Register, MPS/fidelity 하한을 설정하거나 추론할 수 없다.
---
-## 4단계: 목소리 지침 로드
+## 4단계: 기본 목소리와 Persona 로드
```
Read core/voice.md
+Glob custom/personas/{lang}/{persona}.md → 명시한 persona가 있으면 Read
+Read personas/{lang}/{persona}.md → 커스텀이 없을 때
```
+`core/voice.md`는 모든 rewrite에 적용되는 기본 편집 원칙이다. Persona가 생략되면 원문 목소리를 보존한다. Persona가 있으면 YAML frontmatter의 목소리 블록만 적용하고 Markdown 본문은 문서로만 취급한다. Persona는 Document Type, Register, 패턴 정책, MPS/fidelity 하한을 바꿀 수 없다.
+
---
## 4.5단계: 의미 앵커 추출 (Semantic Anchor Extraction)
@@ -159,46 +152,6 @@ Read core/voice.md
---
-## 4.5b단계: 톤 자동 감지 (Tone Auto-Detection)
-
-**실행 조건:** `resolved_tone == "auto"` 일 때만 실행한다. `resolved_tone`이 null이거나 명시 톤(`casual`/`professional`)이면 이 단계를 건너뛴다.
-
-**짧은 입력 예외:** 텍스트가 단락 < 2 OR 전체 문장 < 2이면 감지를 실행하지 않는다 → `professional` 톤으로 기록, `tone_source: skipped_short_input`, `tone_confidence: low`, `tone_evidence: ["input too short"]`. 이것은 폴백이 아니라 감지 시작 안 함이다 (A5 위배 아님). `skipped_short_input` 은 감지를 **건너뛴** 경우 전용 라벨이며, 감지가 실행되었으나 잔류 규칙으로 떨어진 경우(아래 잔류 규칙 항목)와 명확히 구분된다.
-
-**휴리스틱 신호 (각 신호가 트리거되면 해당 tone 버킷에 +1점):**
-
-| 신호 | 트리거 조건 | 귀속 톤 |
-|------|------------|---------|
-| **어휘: casual-ko** | `진짜`, `솔직히`, `근데`, `뭐`, `어`, `걍`, `좀`, `그냥`, `ㅋ`, `ㅎ` 중 3개 이상 | casual |
-| **어휘: casual-en** | 축약형(`don't`, `I'm`, `it's`, `can't`, `won't`) 2개 이상 OR `honestly`, `actually`, `kinda`, `tbh` 등장 | casual |
-
-**잔류(residual) 규칙:** 위 신호 중 어느 버킷도 임계값(1점)을 넘기지 못하면 → `professional` (중립 기본값).
-- `tone_source: auto` (감지는 실행되었음 — short-input 의 `skipped_short_input` 과 구분)
-- `tone_evidence: ["no signal cluster reached threshold; residual default"]`
-- `tone_confidence: low`
-
-**신뢰도 버킷:**
-```
-high: top-bucket score >= 2 × runner-up
-medium: top-bucket score >= 1.3 × runner-up
-low: otherwise (여전히 단일 톤으로 확정 — A5)
-```
-
-**출력:** 내부 `` 블록으로만 기록. 사용자 대면 본문에 절대 포함하지 않는다.
-
-```
-
-tone: casual
-tone_source: auto
-tone_evidence: ["casual-ko lexical hit: 진짜/솔직히/근데", "first-person-ratio: 0.62"]
-tone_confidence: high
-
-```
-
-감지 완료 후 결과 톤(`casual`/`professional`)은 어투(레지스터)에만 반영한다. 톤은 프로필을 고르지 않으며, 장르 프로필은 `--profile` 로만 결정된다 (백본 매핑 제거, 6.0).
-
----
-
## 4.6단계: 통계 기반 의심 구간 탐지 (Stylometric Suspect Zone Detection)
패턴 카탈로그가 명명하지 못하는 분포적 AI다움(균질한 문장 길이, 빈약한 어휘 다양성)을 결정론적 통계로 미리 표시한다. 4.5단계 의미 앵커와 마찬가지로 이 결과는 내부 작업 메모리이며 사용자 대면 출력에 포함하지 않는다.
@@ -378,17 +331,20 @@ document is SUSPECT iff
**파악 항목 (각 한 줄, 내부 작업 메모리):**
-1. **문서 종류** — 랜딩페이지 / 블로그 / 공지 / 문서 / 에세이 중 무엇인가
-2. **화자와 독자** — 누가 누구에게 말하는 글인가
-3. **지배 어투** — 해요체 / 합쇼체(-습니다) / 평서체(-다) 중 어느 것이 지배적인가. 한국어는 문장 종결어미 분포를 직접 세어 판정한다(-니다/-니까 → 합쇼체, -요/-죠 → 해요체, 그 외 -다 → 평서체). 60% 이상 점유한 어투가 없으면 "혼합"으로 판정하고, 문서 성격에 맞는 어투 하나를 선택한다
-4. **핵심 도메인 용어** — 글이 반복 사용하는 고유 표현 목록
+1. **문서 종류와 목적** — 활성 Document Type과 실제 입력이 랜딩 페이지, 블로그, 공지, 기술 문서, 학술문, 이메일, 에세이 중 어디에 해당하며 무엇을 달성해야 하는가
+2. **화자와 독자** — 누가 누구에게 말하는 글이며, 원문이 유지하는 관계와 관점은 무엇인가
+3. **구조 관습** — 제목 계층, CTA, 표·목록·코드, 근거 배치 등 해당 문서에서 고정해야 할 형식은 무엇인가
+4. **지배 어투** — 해요체 / 합쇼체(-습니다) / 평서체(-다) 중 어느 것이 지배적인가. 한국어는 문장 종결어미 분포를 직접 세어 판정한다(-니다/-니까 → 합쇼체, -요/-죠 → 해요체, 그 외 -다 → 평서체). 60% 이상 점유한 어투가 없으면 "혼합"으로 판정하고, 문서 성격에 맞는 어투 하나를 선택한다
+5. **핵심 도메인 용어** — 글이 반복 사용하는 고유 표현 목록
**5단계 적용 규칙:**
- 5a/5b의 모든 재작성은 이 브리프 프레임 안에서 수행한다
- 재작성 문장 전체를 지배 어투 하나로 통일한다 — **문장 간 어투 혼용 자체가 AI 신호다**
- 핵심 용어는 일반 동의어로 치환하지 않고 글의 표현을 그대로 재사용한다
-- `--tone` 이 명시된 경우(4.5b 참조) 톤은 그 지시를 따르되, 어투 통일 규칙은 동일하게 적용한다
+- `--register`가 명시되면 그 값을 따르되, 어투 통일 규칙은 동일하게 적용한다
+- `--persona`가 명시되면 그 Persona의 목소리 블록만 적용하고, 생략되면 원문 목소리를 보존한다
+- Document Type이 정한 목적·독자·구조·스타일·회피 규칙을 따르되, 원문에 없는 주장·사례·인물·성과·CTA 약속을 만들지 않는다
> **주의:** 브리프는 4.5단계 앵커와 동일한 내부 작업 메모리다. 사용자 대면 출력에 포함하지 않는다.
> **CLI 동기화:** Node CLI는 같은 단계를 프롬프트로 수행한다 — 지배 어투는 `src/features/stylometry.js#detectKoreanRegister`가 결정론으로 측정해 "문서 신호" 섹션으로 주입하고, 브리프 지시는 rewrite 프롬프트(minimal·strict 모두)에 포함된다.
@@ -398,7 +354,7 @@ document is SUSPECT iff
## Strict 모드 (다중 패스) (`--strict`)
-`--strict` 플래그가 있으면 아래 5단계 패스를 순차적으로 수행한다. 1~4단계(설정, 패턴, 프로필, 목소리 로드)는 한 번만 실행한다.
+`--strict` 플래그가 있으면 아래 5단계 패스를 순차적으로 수행한다. 1~4단계에서 설정·패턴·Document Type·기본 목소리·Persona를 한 번만 로드한다.
**실행 모드 (자동 선택):**
@@ -412,13 +368,13 @@ document is SUSPECT iff
#### P1: 전체 감지 패스 (Detection Pass)
-4.x단계(4.5 의미 앵커 추출, 4.5b 톤 감지, 4.6 통계 기반 의심 구간, 4.7 AI-lexicon 매칭, 4.8 문서 브리프)를 완전히 실행한다. 결과는 심각도가 부여된 발견 목록(`findings list`)으로 정리한다.
+4.x단계(4.5 의미 앵커 추출, 4.6 통계 기반 의심 구간, 4.7 AI-lexicon 매칭, 4.8 문서 브리프)를 완전히 실행한다. 결과는 심각도가 부여된 발견 목록(`findings list`)으로 정리한다.
**위임 모드:** 이 패스를 `patina-detector` 서브에이전트에 위임한다(반환: 심각도가 부여된 발견 목록). 폴백 모드에서는 메인 스킬이 위 4.x단계를 직접 실행한다.
- 각 발견 항목: `{paragraph_index, span, signal_type, severity}` 형태 (내부 작업 메모리)
- `signal_type`: `burstiness_low` | `MATTR_low` | `lexicon_hot` | `pattern_match`
-- 발견이 0건이면 즉시 종료 — 원문을 그대로 출력하고 Strict 모드 결과 footer를 붙인다
+- 발견이 0건이면 즉시 종료하고 원문만 출력한다
#### P2: 근거 기반 재작성 (Evidence-Based Rewrite)
@@ -474,31 +430,20 @@ retry 조건 (accept 실패 시, 재시도 횟수 < 3):
rollback 조건 (retry 상한 초과 또는 retry 후에도 accept 실패):
offending 스팬의 교정을 취소하고 해당 구간을 원문으로 복원한다
나머지 통과된 스팬의 교정은 유지한다
- 롤백 사유를 footer에 보고한다
+ 롤백 사유는 내부 진단에 기록하고 사용자 본문에는 넣지 않는다
```
### 출력 형식
-P5 게이트 결과 이후 최종 교정 텍스트와 함께 다음 footer를 출력한다 (6단계 공통 YAML footer에 추가):
-
-```
----
-strict_mode: true
-passes: [P1, P2, P3, P4, P5]
-fidelity_score: <값>
-mps_score: <값>
-residual_hot_zones: <개수>
-over_edit:
-retries: <횟수>
-rollback_spans: <롤백된 스팬 수 또는 0>
-gate_result: accept | rollback
----
-```
+P5 게이트 이후 수락되거나 부분 롤백된 최종 본문만 출력한다.
+`strict_mode`, 점수, 재시도 수, 롤백 스팬, 게이트 결과는 내부 진단으로
+유지하며 사용자 본문이나 YAML footer로 붙이지 않는다. 구조화된 출력
+환경에서만 본문 밖의 별도 필드로 제공한다.
## 배치 모드 (`--batch`)
-`--batch` 플래그가 있으면 아래 절차를 따른다. 1~4단계(설정, 패턴, 프로필, 목소리 로드)는 한 번만 실행한다.
+`--batch` 플래그가 있으면 아래 절차를 따른다. 1~4단계의 설정·패턴·Document Type·기본 목소리·Persona는 한 번만 로드한다.
### 입력
@@ -538,7 +483,7 @@ Glob {지정된 파일 패턴} → 파일 목록 확보
## 5단계: 텍스트 처리
-로드된 패턴, 프로필, 목소리 지침을 조합하여 텍스트를 처리한다. 처리는 3개 하위 단계로 나뉜다.
+로드된 패턴, Document Type 정책, 기본 목소리, 선택적 Persona와 Register를 조합하여 텍스트를 처리한다. 처리는 3개 하위 단계로 나뉜다.
### 5a단계: 구조 분석 (Phase 1)
@@ -602,22 +547,11 @@ FOR each anchor IN anchor_list:
1. **AI 패턴 식별** - 로드된 문장/어휘 패턴 팩의 모든 패턴을 스캔
2. **문제 구간 다시 쓰기** - AI스러운 표현을 토큰 단위로 치환하지 말고, 문맥을 읽은 뒤 절/문장 단위로 자연스럽게 다시 쓴다
3. **의미 보존** - 핵심 메시지를 유지
-4. **어조 맞추기** - 프로필의 어조 지침에 따라 톤 조절
-5. **개성 불어넣기** - voice.md의 지침에 따라 실제 사람의 목소리를 넣기
+4. **Register 적용** - 명시값이 있으면 `casual | professional` 격식 수준만 적용하고, 없으면 원문 레지스터를 보존한다
+5. **목소리 적용** - 기본 지침을 따르고, Persona가 있으면 그 목소리 특성만 적용한다. Persona가 없으면 원문 목소리를 보존한다
6. **blocklist/allowlist 적용** - 설정의 blocklist 어휘도 추가 감지, allowlist 어휘는 감지에서 제외
-7. **프로필 오버라이드 적용** - 프로필에 `pattern-overrides`가 있으면 해당 패턴의 교정 강도를 조절 (suppress/reduce/amplify)
-8. **톤 파생 오버라이드 적용 (v3.10)** — `resolved_tone != null`이면 프로필 오버라이드 위에 톤별 오버라이드를 추가 적용한다. 충돌 시 톤 오버라이드가 우선한다.
-
- > **Override stack 규칙 (H5):** 동일 instruction이 두 레이어(profile + tone)에서 발화하면 한 번만 적용한다 (멱등). 톤 오버라이드는 프로필 오버라이드를 **대체(replace)** 하는 것이지 **중첩(stack)** 이 아니다. 즉, 같은 패턴 번호에 대해 profile=`reduce`, tone=`amplify` 가 동시 지정되면 최종은 `amplify` 단일이며 `reduce` × `amplify` 같은 누적 연산은 일어나지 않는다.
-
- | Tone | ko 오버라이드 | en 오버라이드 |
- |------|-------------|-------------|
- | `casual` | 14:suppress, 15:reduce, 17:reduce, 18:amplify, 8:amplify | 14:suppress, 15:reduce, 17:reduce, 7:amplify, 8:amplify |
- | `professional` | 17:suppress, 25:reduce, 28:amplify | 17:suppress, 25:reduce, 28:amplify |
-
- **legal/medical fidelity 강제 (R2):** resolved_tone=professional이고 config.profile이 `legal` 또는 `medical`이면, `scoring.combined-weights.legal` / `scoring.combined-weights.medical` (fidelity 0.65)을 강제 적용한다. 톤 오버라이드가 fidelity 하한을 낮추지 않도록 한다.
-
-9. **의미 보존 제약 주입** — 의미 위험도가 HIGH인 패턴을 적용할 때, 해당 문단의 앵커를 교정 프롬프트에 포함한다: "다음 주장을 반드시 유지하라: {앵커 목록}". MEDIUM 위험도 패턴은 극성(Polarity) 또는 부정(Negation) 앵커가 있는 문단에서만 제약을 주입한다. LOW 위험도 패턴은 제약 없이 적용한다.
+7. **Document Type 패턴 정책 적용** - `pattern-overrides`에 따라 해당 패턴의 교정 강도를 조절한다 (`suppress | reduce | amplify`). 이 정책은 목소리나 레지스터를 바꾸지 않는다
+8. **의미 보존 제약 주입** — 의미 위험도가 HIGH인 패턴을 적용할 때, 해당 문단의 앵커를 교정 프롬프트에 포함한다: "다음 주장을 반드시 유지하라: {앵커 목록}". MEDIUM 위험도 패턴은 극성(Polarity) 또는 부정(Negation) 앵커가 있는 문단에서만 제약을 주입한다. LOW 위험도 패턴은 제약 없이 적용한다.
**CJK 절/문장 단위 교정 가드 (issue #352):** `--lang ko|zh|ja`에서는 구두점이나 단어 1개만 1:1로 바꾸지 않는다. em dash, 콜론, 세미콜론, 슬래시, 쉼표 접속, 괄호식 삽입구처럼 절 관계를 표시하는 구두점이 AI 신호와 함께 보이면, 문장 전체를 읽고 목표 언어의 자연스러운 절 구조·문장 분리·접속 표현으로 다시 짠다. 번역체/직역 명사구가 구두점에 붙어 있으면 둘을 함께 고친다. 한국어 예: `무 TUI` 식 직역은 `TUI 없이 완전 자율로 설치하려면 ...`처럼 풀고, `"끝난 것 같아요"로는 부족한 열린 작업`은 `"끝난 것 같아요"만으로는 부족한, 결과를 끝까지 확인해야 하는 열린 작업`처럼 절 관계를 드러낸다. 교정 중 행위자·극성·조건·숫자·인과는 유지한다.
@@ -645,43 +579,26 @@ FOR each anchor IN anchor_list:
## 6단계: 출력
-**모든 출력 모드 공통 — YAML footer (v3.10):**
+**출력 채널 분리:**
-모든 출력 모드(rewrite, diff, audit, score)의 최종 결과 끝에 다음 YAML footer 블록을 추가한다. 이것이 tone 정보의 **유일한** 노출 지점이다. rewrite 본문에 `[tone: ...]` 같은 인라인 표기를 절대 포함하지 않는다 (A7).
-
-```
----
-tone:
-tone_source: user | auto | unsupported_language_fallback | profile_only | skipped_short_input
-tone_evidence: ["<신호 1>", "<신호 2>"]
-tone_confidence: low | medium | high
----
-```
-
-- `tone_source: profile_only`: `--tone` 미사용 + config `tone:` 미설정 (profile-only 모드)
-- `tone_source: user`: 명시적 `--tone` 또는 config `tone:` 으로 지정
-- `tone_source: auto`: `--tone auto` 또는 `tone: auto` 로 자동 감지 수행 (잔류 규칙으로 떨어진 경우 포함)
-- `tone_source: unsupported_language_fallback`: zh/ja에서 명시 톤 사용 시
-- `tone_source: skipped_short_input`: 단락<2 OR 문장<2 이라 4.5b 감지를 시작 안 한 경우 (auto 요청이었지만 검출 자체가 실행되지 않음)
+- rewrite의 text/markdown 출력에는 최종 사용자 본문만 쓴다. 축 메타데이터, YAML footer, 분석 라벨을 붙이지 않는다.
+- diff/audit/score는 해당 모드의 결과만 출력하며 공통 footer를 붙이지 않는다.
+- 구조화된 JSON 출력이 필요한 환경에서는 `documentType`, `persona`, `register`를 본문 밖의 독립 필드로 제공한다.
+- Register가 생략되면 `register: null`; 명시되면 `register`, `register_source`, `register_evidence`, `register_confidence`를 별도 객체로 제공한다.
### rewrite 모드 (기본)
-다음을 제공한다:
-1. 초안
-2. "아래 글에서 AI가 쓴 것처럼 보이는 부분은?" (간단한 목록)
-3. 최종본
-4. 변경 사항 요약 (필요시)
-5. YAML footer (위 공통 규칙 적용)
+최종 교정본만 제공한다. 내부 초안, 자기검수, 패턴 목록, 축 메타데이터는 출력하지 않는다.
-> **주의 (A7):** rewrite 최종본 본문에 tone 정보를 언급하거나 삽입하지 않는다. "이 텍스트는 casual 톤으로 재작성되었습니다" 같은 내러티브 인터젝션 금지.
+> **주의:** rewrite 본문에 Document Type, Persona, Register 정보를 언급하거나 삽입하지 않는다.
### diff 모드 (`--diff`)
-변경 사항을 패턴별로 표시한다. 뭘 왜 바꿨는지 보여준다. 끝에 YAML footer 추가.
+변경 사항을 패턴별로 표시한다. 뭘 왜 바꿨는지 보여준다.
### audit 모드 (`--audit`)
-감지만 하고 수정하지 않는다. 패턴별 발견 위치와 심각도를 테이블로 출력한다. 끝에 YAML footer 추가.
+감지만 하고 수정하지 않는다. 패턴별 발견 위치와 심각도를 테이블로 출력한다.
### score 모드 (`--score`)
@@ -689,7 +606,7 @@ AI 유사도 점수를 0-100 척도로 산출한다. `core/scoring.md`를 참조
1. **패턴 감지**: audit 모드와 동일하게 모든 패턴을 스캔하고, 감지된 각 패턴에 대해
severity를 부여한다 (`core/scoring.md`의 심각도 루브릭 참조)
-2. **프로필 오버라이드 적용**: pattern-overrides가 있으면 심각도를 조정한다
+2. **Document Type 패턴 정책 적용**: `pattern-overrides`가 있으면 심각도를 조정한다
- `amplify`: 심각도 × 1.5 (최대 3)
- `reduce`: 심각도 × 0.5
- `suppress`: 심각도 = 0 (해당 패턴 건너뜀)
@@ -712,9 +629,6 @@ AI 유사도 점수를 0-100 척도로 산출한다. `core/scoring.md`를 참조
| structure | 0.15 | 1/5 | 20.0 | 3.0 |
| viral-hook | 0.10 | 0/9 | 0.0 | 0.0 |
| **전체** | | | | **13.3 (±10)** |
-| **tone** | — | — | casual | auto/high |
-
-> **참고:** 카테고리 점수는 소수점으로 계산하고, 표시는 소수 첫째 자리까지 반올림한다. tone 행은 resolved_tone과 tone_source/tone_confidence를 표시한다. 끝에 공통 YAML footer를 추가한다.
점수 해석: 0-15 사람다움 / 16-30 거의 사람다움 / 31-50 혼재 / 51-70 AI 느낌 / 71-100 AI 생성
@@ -724,7 +638,7 @@ AI 유사도 점수를 0-100 척도로 산출한다. `core/scoring.md`를 참조
1. **Claims Preserved** — 원본의 사실적 주장이 교정본에 보존되었는지 (0-3)
2. **No Fabrication** — 교정본에 원본에 없는 내용이 추가되지 않았는지 (0-3)
-3. **Tone Match** — 문체/격식 수준이 일치하는지 (0-3, 프로필 오버라이드 고려)
+3. **Audience/Register Match** — 문서 기능과 독자 관계를 보존했는지 평가한다. 명시적 `--register`가 있으면 그 목표를 기준으로 삼는다 (0-3)
4. **Length Ratio** — 길이 비율이 적절한지 (0-3, 결정론적 계산)
| 지표 | 점수 |
@@ -737,7 +651,7 @@ AI 유사도 점수를 0-100 척도로 산출한다. `core/scoring.md`를 참조
의미 보존 점수(MPS)는 4.5단계에서 추출된 의미 앵커가 최종 결과물에 얼마나 보존되었는지를 측정한다. `core/scoring.md` §14를 참조한다.
종합 점수 = `(AI 유사도 × ai_weight) + ((100 - 충실도) × fidelity_weight)`.
-가중치는 `scoring.combined-weights.{profile}` 설정에 따른다 (기본: AI 0.60, 충실도 0.40).
+가중치는 `scoring.combined-weights.{document-type}` 설정에 따른다 (기본: AI 0.60, 충실도 0.40).
> **참고:** 점수는 LLM의 심각도 판단에 기반하므로 ±8-10 포인트의 변동이 있을 수 있다.
> 정확한 수치보다 범위로 해석한다.
diff --git a/agents/patina-fidelity-auditor.md b/agents/patina-fidelity-auditor.md
index acfa8f1..b755871 100644
--- a/agents/patina-fidelity-auditor.md
+++ b/agents/patina-fidelity-auditor.md
@@ -1,6 +1,6 @@
---
name: patina-fidelity-auditor
-description: Triggers to audit whether a patina rewrite preserved meaning versus the original text. Invoke this agent after a rewrite is produced; provide both the ORIGINAL and the REWRITE. It checks all four fidelity criteria from core/scoring.md §§9-14 (claims, fabrication, tone, length) and returns a pass/needs-rollback verdict with offending spans identified.
+description: Triggers to audit whether a patina rewrite preserved meaning versus the original text. Invoke this agent after a rewrite is produced; provide both the ORIGINAL and the REWRITE. It checks all four fidelity criteria from core/scoring.md §§9-14 (claims, fabrication, audience/register, length) and returns a pass/needs-rollback verdict with offending spans identified.
model: sonnet
tools: Read
---
@@ -44,11 +44,12 @@ Check every claim in the REWRITE:
Score: High (3) / Medium (2) / Low (1) / Fail (0) per `core/scoring.md` §10.2 rubric.
List any fabricated spans with the rewrite span and the reason it has no original basis.
-### 10.3 Tone Match (`core/scoring.md` §10.3)
+### 10.3 Audience/Register Match (`core/scoring.md` §10.3)
-Compare register: formality level, domain (academic / technical / casual / etc.), and intended audience.
-- If a profile was active that explicitly shifts register, score against the profile target, not the original.
-- Mixed register (e.g., formal opening, casual middle) counts as Low.
+Compare document function, intended audience, and register.
+- If an explicit `--register` target was supplied, score against that target.
+- Persona voice and Document Type policy do not silently override Register.
+- Mixed register (for example, a formal opening and casual middle) counts as Low.
Score: High (3) / Medium (2) / Low (1) / Fail (0) per `core/scoring.md` §10.3 rubric.
@@ -77,7 +78,7 @@ These are not scored separately but must be explicitly confirmed or flagged:
## Fidelity scoring formula (`core/scoring.md` §12)
```
-fidelity_score = ((claims + fabrication + tone + length) / 12) × 100
+fidelity_score = ((claims + fabrication + audience_register + length) / 12) × 100
```
Verification floors (`core/scoring.md` §13): fidelity_score ≥ `verification.fidelity-floor` (default: 70) is required; a result below the floor must be retried or rolled back regardless of AI-likeness improvement.
@@ -98,7 +99,7 @@ Original length: {n} chars Rewrite length: {m} chars Ratio: {r}%
CRITERION SCORES
Claims preserved: {score}/3 — {brief rationale}
No fabrication: {score}/3 — {brief rationale}
- Tone match: {score}/3 — {brief rationale}
+ Audience/register: {score}/3 — {brief rationale}
Length ratio: {score}/3 — ratio={r}%
Fidelity score: ({sum}/12) × 100 = {fidelity_score}
diff --git a/agents/patina-naturalness-reviewer.md b/agents/patina-naturalness-reviewer.md
index eeb8bd5..ba211d6 100644
--- a/agents/patina-naturalness-reviewer.md
+++ b/agents/patina-naturalness-reviewer.md
@@ -45,7 +45,7 @@ Compare the rewrite to the ORIGINAL (if provided). Flag over-editing when:
- Paragraphs with no detected AI patterns in the original have been changed.
- The genre has shifted (e.g., a report has become an essay, a technical doc has become narrative).
-- The register has drifted beyond what the active profile permits.
+- The register has drifted from the source or an explicit `--register` target.
- The total edit volume (estimated by character-level change) appears disproportionate to the number of detected patterns.
Over-editing is a concern even when individual changes look locally natural. Report specific over-edited spans with the original and rewrite versions.
diff --git a/assets/demo/README.md b/assets/demo/README.md
index 14d8673..d759284 100644
--- a/assets/demo/README.md
+++ b/assets/demo/README.md
@@ -16,7 +16,7 @@ Current assets:
- `patina-playground-en.png` — expanded-result still used where PNG is required.
- `patina-preview-en.gif` — CLI `--preview` animation, linked from the README demo section and used by the Korean, Chinese, and Japanese READMEs.
- source: a styled local HTML page with Notion-template-pack prose.
- - generated with: `node bin/patina.js --preview --lang en --tone marketing --backend codex-cli .html`
+ - generated with: `node bin/patina.js --preview --lang en --document-type marketing --backend codex-cli .html`
- captured views: Rewritten → Diff → Original → Both → Diff.
- expected visual contract: page layout stays fixed; prose blocks are numbered; the bar shows `4 OF 5 BLOCKS REWRITTEN` and `SCORE 60 → 0`; Diff view uses red strikethrough removals and green insertions.
- size target: keep the GIF under 10 MB so GitHub renders it reliably; current target is under 1 MB.
@@ -36,7 +36,7 @@ Shared requirements:
2. Run:
```bash
-node bin/patina.js --preview --lang en --tone marketing --backend codex-cli /tmp/patina-preview-sample.html
+node bin/patina.js --preview --lang en --document-type marketing --backend codex-cli /tmp/patina-preview-sample.html
```
3. Open the saved preview HTML from stderr.
diff --git a/core/scoring.md b/core/scoring.md
index bc97327..820ae6b 100644
--- a/core/scoring.md
+++ b/core/scoring.md
@@ -133,9 +133,9 @@ Weights are configurable via `scoring.category-weights.{lang}` in `.patina.yaml`
---
-## 5. Profile Override Adjustments
+## 5. Document-Type Pattern Policy
-Before summing severities, apply profile `pattern-overrides` modifiers:
+Before summing severities, apply the active document type's `pattern-overrides`:
| Override | Factor | Effect |
|----------|--------|--------|
@@ -144,8 +144,8 @@ Before summing severities, apply profile `pattern-overrides` modifiers:
| suppress | × 0.0 | Excludes pattern entirely |
| normal (default) | × 1.0 | No change |
-Example: blog profile suppresses #14 (bold) → pattern #14 severity becomes 0,
-excluded from ko-style category calculation.
+Example: the `blog` document type suppresses #14 (bold), so pattern #14
+contributes 0 to the ko-style category.
### Language-Scoped Overrides
@@ -154,7 +154,7 @@ cross-language number collisions (e.g., ko #8 is "~적 접미사" while en #8 is
"Copula Avoidance" — the same number refers to unrelated patterns in each language).
```yaml
-# Language-scoped format (recommended for multi-language profiles)
+# Language-scoped format (recommended for multilingual document types)
pattern-overrides:
ko:
8: amplify # ko-language #8 (~적 접미사)
@@ -179,7 +179,7 @@ category_score = (sum of adjusted severities / (pattern_count × 3)) × 100
```
- `sum of adjusted severities`: sum severity points for all detected patterns in category,
- after applying profile override factors
+ after applying document-type pattern factors
- `pattern_count × 3`: maximum possible score (all patterns detected at High severity)
- Result: 0-100 per category
@@ -189,7 +189,7 @@ category_score = (sum of adjusted severities / (pattern_count × 3)) × 100
overall_score = Σ(category_score × category_weight) for all categories
```
-### Worked Example (Korean, default profile)
+### Worked Example (Korean, default document type)
Input text detected patterns:
@@ -315,22 +315,19 @@ The output does not add claims, facts, or specifics not present or implied by th
| Low | 1 | Noticeable fabrication — specific numbers, names, or claims not in the original |
| Fail | 0 | Significant fabrication — output contains substantial invented content |
-### 10.3 Tone Match
+### 10.3 Audience/Register Match
-The output serves the same audience and domain register as the original (or the
-profile's target register, if explicitly overridden). This judges function, not
-surface polish: stripping AI-ish formality, hype, or ceremony while the audience
-and domain hold is High, not drift.
+The output serves the same audience and document function as the original.
+When the user explicitly supplies `--register`, assess against that requested
+register. Judge function rather than AI-ish stiffness, hype, or ceremony.
| Level | Points | Criteria |
|-------|--------|----------|
| High | 3 | Same audience and domain — a policy notice still reads as a policy notice, a product page as a product page |
| Medium | 2 | Slight drift — somewhat more/less formal, but still appropriate for the context |
-| Low | 1 | Noticeable mismatch — formal original made casual (or vice versa) without profile justification |
+| Low | 1 | Noticeable mismatch — formal original made casual (or vice versa) without an explicit register override |
| Fail | 0 | Register violation — academic text made into slang, or casual text made into legalese |
-**Profile exception:** When a profile explicitly shifts register (e.g., blog profile amplifies
-informality), tone match is assessed against the *profile target*, not the original register.
### 10.4 Length Ratio
@@ -363,10 +360,10 @@ To reduce variance, apply these guidelines when scoring fidelity criteria:
- Adding a commonly-known context note ("Seoul, the capital of South Korea") → Medium.
- Inventing a statistic, date, or name not in the original → Low or Fail.
-### Tone Match
-- Compare the first and last paragraphs of original vs. output for audience and domain cues.
-- Removing hype, ceremony, or AI-ish formality is the rewrite's job → High.
-- Profile-targeted register shifts are expected, not penalized.
+### Audience/Register Match
+- Compare the first and last paragraphs for audience, function, and register cues.
+- Removing hype, ceremony, or AI-ish stiffness is the rewrite's job → High.
+- An explicit register override is expected and is not penalized.
- Mixed register (formal opening, casual middle) counts as Low.
### Length Ratio
@@ -383,7 +380,7 @@ Each criterion is scored 0–3 (same as AI-likeness severity). The fidelity scor
across all four criteria:
```
-fidelity_score = ((claims + fabrication + tone + length) / 12) × 100
+fidelity_score = ((claims + fabrication + audience_register + length) / 12) × 100
```
- Maximum: (3+3+3+3) / 12 × 100 = **100** (perfect fidelity)
@@ -403,7 +400,7 @@ Output: Humanized version.
|-----------|-------|-----------|
| Claims preserved | 3 (High) | All policy recommendations and cited figures present |
| No fabrication | 2 (Medium) | Added "as widely reported" — minor inference stated as fact |
-| Tone match | 3 (High) | Academic audience and domain held throughout |
+| Audience/register match | 3 (High) | Academic audience and document function held throughout |
| Length ratio | 3 (High) | Output is 68% of original length (within the 50–130% band) |
Fidelity = (3+2+3+3) / 12 × 100 = **91.7**
@@ -446,17 +443,17 @@ Where:
| Context | AI Weight | Fidelity Weight | Rationale |
|---------|-----------|-----------------|-----------|
| Default | 0.60 | 0.40 | Balanced — humanization is primary goal |
-| Academic profile | 0.40 | 0.60 | Meaning preservation is critical in scholarly work |
-| Blog profile | 0.70 | 0.30 | Creative rewriting tolerated |
-| Technical profile | 0.35 | 0.65 | Accuracy is paramount in docs |
-| Social profile | 0.75 | 0.25 | Tone transformation expected |
-| Email profile | 0.50 | 0.50 | Equal importance |
-| Legal profile | 0.35 | 0.65 | Legal precision must be preserved |
-| Medical profile | 0.35 | 0.65 | Clinical accuracy is critical |
-| Marketing profile | 0.65 | 0.35 | Tone transformation tolerated, creative rewriting expected |
-| Namuwiki profile | 0.65 | 0.35 | Tone transformation tolerated for wiki-style cleanup |
-
-Configurable via `scoring.combined-weights.{profile}` in `.patina.yaml`:
+| Academic document type | 0.40 | 0.60 | Meaning preservation is critical in scholarly work |
+| Blog document type | 0.70 | 0.30 | Pattern cleanup may be more liberal |
+| Technical document type | 0.35 | 0.65 | Accuracy is paramount in docs |
+| Social document type | 0.75 | 0.25 | Fragments and platform conventions are legitimate |
+| Email document type | 0.50 | 0.50 | Equal importance |
+| Legal document type | 0.35 | 0.65 | Legal precision must be preserved |
+| Medical document type | 0.35 | 0.65 | Clinical accuracy is critical |
+| Marketing document type | 0.65 | 0.35 | Persuasive conventions are legitimate |
+| Namuwiki document type | 0.65 | 0.35 | Wiki-style structural conventions are legitimate |
+
+Configurable via `scoring.combined-weights.{document-type}` in `.patina.yaml`:
```yaml
scoring:
combined-weights:
diff --git a/core/standalone-prompt.md b/core/standalone-prompt.md
index 69d29b2..502b257 100644
--- a/core/standalone-prompt.md
+++ b/core/standalone-prompt.md
@@ -8,7 +8,7 @@ description: Agent-agnostic humanization prompt template for any LLM
You are an editor who detects and removes AI writing patterns from text, rewriting it into natural, human-written prose.
-This template is **agent-agnostic** — it can be sent to any LLM API, chat interface, or agent framework. The host system is responsible for assembling the components (config, patterns, profile, voice) into the prompt before sending.
+This template is **agent-agnostic** — it can be sent to any LLM API, chat interface, or agent framework. The host system assembles configuration, pattern packs, document policy, core voice, and an optional Persona before sending.
---
@@ -19,7 +19,9 @@ The user provides:
```yaml
config:
language: ko # ko | en | zh | ja
- profile: default # default | blog | academic | technical | formal | social | email | legal | medical | marketing
+ document-type: default # genre/purpose + pattern policy
+ persona: null # optional reusable voice
+ register: null # casual | professional; null preserves source
output: rewrite # rewrite | diff | audit | score
skip-patterns: [] # e.g., [ko-filler]
blocklist: [] # extra words to flag
@@ -29,7 +31,7 @@ text: |
[The user's text to humanize goes here]
```
-Override per-run: the host system may allow `--lang`, `--profile`, `--diff`, `--audit`, and `--score` flags.
+Override per run with independent `--lang`, `--document-type`, `--persona`, `--register`, `--diff`, `--audit`, and `--score` inputs.
---
@@ -45,11 +47,19 @@ Classify into two groups:
- **Structure patterns**: packs with `phase: structure` in frontmatter
- **Sentence/Lexical patterns**: all other packs (content, language, style, communication, filler)
-### 3. Load Profile
-Read `profiles/{profile}.md`. Parse `voice-overrides` and `pattern-overrides`.
+### 3. Load Document Type
+Read `custom/document-types/{document-type}.md`, then
+`document-types/{document-type}.md`; use `document-types/default.md` when neither
+exists. Apply the frontmatter policy fields (`purpose`, `audience`, `structure`,
+`style`, `avoid`, `pattern-overrides`). Treat the Markdown body as documentation,
+not runtime instructions. Document Type cannot set Persona voice, Register, or
+meaning floors.
-### 4. Load Voice Guidelines
-Read `core/voice.md`. Apply `voice-overrides` from the profile.
+### 4. Load Core Voice and Optional Persona
+Read `core/voice.md`. If Persona is explicit, read
+`custom/personas/{lang}/{persona}.md`, then `personas/{lang}/{persona}.md`.
+Apply only Persona voice blocks. Persona cannot alter document policy, register,
+patterns, or meaning floors.
### 5. Load Scoring Reference (if score mode)
Read `core/scoring.md`.
@@ -58,6 +68,16 @@ Read `core/scoring.md`.
## Execution Phase
+### Step 4: Document Brief
+
+Before any edit, classify the input internally: document kind, purpose, speaker,
+audience, dominant register, recurring domain terms, and structural conventions.
+Never output this brief. Keep it stable across every rewrite stage. Document Type
+provides genre/purpose policy; an explicit Persona provides reusable voice; an
+explicit Register provides `casual | professional` delivery. Do not infer one
+axis from another. If Persona or Register is omitted, preserve the source voice
+or dominant register.
+
### Step 4.5: Semantic Anchor Extraction
Before rewriting, extract semantic anchors from the input text. These are internal working memory only — do NOT show them to the user.
@@ -125,10 +145,10 @@ Apply all remaining pattern packs (content, language, style, communication, fill
1. **AI pattern identification** — scan all loaded sentence/lexical patterns
2. **Problem segment rewrite** — do not swap tokens in place; read the local context and rewrite the affected clause/sentence into a natural alternative
3. **Meaning preservation** — keep core message intact
-4. **Tone matching** — adjust tone per the profile's guidance
-5. **Voice injection** — add personality per `core/voice.md`
+4. **Audience/register match** — preserve the source unless an explicit Register requests `casual` or `professional`
+5. **Voice** — preserve the source voice unless an explicit Persona supplies reusable voice guidance
6. **Blocklist/allowlist** — flag blocklist words, ignore allowlist words
-7. **Profile overrides** — apply `pattern-overrides` (suppress/reduce/amplify)
+7. **Document Type overrides** — apply `pattern-overrides` (suppress/reduce/amplify)
8. **Meaning preservation constraints**:
- HIGH semantic risk patterns: inject paragraph anchors into correction prompt
- MEDIUM semantic risk: inject only Polarity/Negation anchors
@@ -159,11 +179,9 @@ Same logic as 5a-v. Additionally:
### Rewrite Mode (default)
-Provide:
-1. Draft
-2. "What still looks AI-written?" (brief bullet list)
-3. Final version
-4. Change summary (optional)
+Provide only the final rewritten text. Drafts, self-audit notes, axis metadata,
+and YAML footers are internal. Structured hosts may expose diagnostics as fields
+outside the rewritten text.
### Diff Mode
@@ -206,12 +224,12 @@ Score interpretation:
Fidelity criteria (each 0-3):
- Claims preserved
- No fabrication
-- Tone match (or profile target if overridden)
+- Audience/register match (or the explicit Register when supplied)
- Length ratio (deterministic: output/original length)
Combined = `(ai_likeness × ai_weight) + ((100 - fidelity) × fidelity_weight)`
-Weights per profile (from `scoring.combined-weights` in `.patina.default.yaml`):
+Weights per Document Type (from `scoring.combined-weights` in `.patina.default.yaml`):
- default: AI 0.60, fidelity 0.40
- academic: AI 0.40, fidelity 0.60
- blog: AI 0.70, fidelity 0.30
@@ -226,7 +244,7 @@ Weights per profile (from `scoring.combined-weights` in `.patina.default.yaml`):
## Batch Mode
When processing multiple files:
-1. Load config, patterns, profile, voice once
+1. Load config, patterns, Document Type, core voice, and optional Persona once
2. For each file (max 50KB; skip larger files):
- Read file
- Run pipeline
@@ -268,7 +286,7 @@ category_score = (sum of adjusted severities / (pattern_count × high severity p
overall_score = Σ(category_score × category_weight) for all categories
```
-### Profile Override Factors
+### Document-Type Override Factors
| Override | Factor | Effect |
|----------|--------|--------|
@@ -304,9 +322,9 @@ If no anchors extracted: `MPS = N/A`
- **Preserve meaning**: claims, polarity, causation, quantifiers, negations must survive rewriting.
- **Do not fabricate**: no information not present in the original.
-- **Match profile tone**: or the profile's target tone if explicitly overridden.
-- **Inject voice**: follow `core/voice.md` per language.
-- **Apply overrides**: respect `pattern-overrides` and `voice-overrides`.
+- **Match audience/register**: preserve the source unless an explicit Register was supplied.
+- **Preserve or apply voice**: preserve source voice by default; apply only the explicit Persona's voice blocks.
+- **Apply document policy**: respect the Document Type's `pattern-overrides`.
- **Bounded verification**: self-audit runs once; each anchor has at most one retry before its original sentence is restored.
- **Scores have variance**: ±8-10 points between runs due to LLM severity assignment. Interpret ranges, not exact numbers.
diff --git a/core/voice.md b/core/voice.md
index 565f35e..b016d5a 100644
--- a/core/voice.md
+++ b/core/voice.md
@@ -1,12 +1,13 @@
---
name: voice
description: 개성과 목소리 지침 (Korean, English, Japanese, Chinese)
-version: 1.1.0
+version: 1.2.0
---
# 개성과 목소리
AI 패턴을 피하는 건 절반일 뿐이다. 깨끗하지만 영혼 없는 글도 AI만큼이나 티가 난다.
+아래 항목은 무조건 캐주얼하게 만들라는 지시가 아니라, 문서 맥락상 허용될 때 사람다운 목소리를 살리는 진단 기준이다. 원문에 없던 의견·1인칭·감정·농담·일화·수치·친밀감을 새로 만들지 않는다. `Document Type`은 문서 관습과 패턴 정책, `Persona`는 명시적으로 선택한 재사용 목소리, `Register`는 `casual | professional` 전달 방식만 정한다. Persona나 Register가 생략되면 원문의 목소리와 지배적 레지스터를 보존한다. 학술문·법률문·기술 문서·보도자료·랜딩 페이지에서 필요한 객관성, 전문 용어, 구조, CTA를 캐주얼함으로 바꾸지 말고, 그 문맥 안의 AI 흔적만 제거한다.
## 영혼 없는 글의 징후 (기술적으로 "깨끗"해도):
- 모든 문장의 길이와 구조가 비슷하다
@@ -16,19 +17,19 @@ AI 패턴을 피하는 건 절반일 뿐이다. 깨끗하지만 영혼 없는
- 유머, 날카로움, 개성이 없다
- 보도자료나 백과사전처럼 읽힌다
-## 목소리를 넣는 법:
+## 문맥에 맞는 목소리를 살리는 법:
-**의견을 가져라.** 사실을 나열만 하지 말고 반응해라. "솔직히 이건 좀 복잡하다"가 장단점을 중립적으로 나열하는 것보다 낫다.
+**원문의 태도를 살려라.** 원문이 판단이나 반응을 담고 있다면 중립적인 목록으로 평탄화하지 않는다. 원문에 없는 의견은 만들지 않는다.
-**리듬을 바꿔라.** 짧은 문장. 그리고 좀 느긋하게 풀어가는 긴 문장. 섞어라.
+**문맥 안에서 리듬을 바꿔라.** 문서의 구조 관습과 레지스터를 해치지 않는 범위에서 문장 길이와 구조가 기계적으로 반복되지 않게 한다.
-**복잡성을 인정해라.** 진짜 사람은 복잡한 감정을 가진다. "인상적이긴 한데 좀 불안하다"가 "인상적이다"보다 사람답다.
+**원문의 복잡성을 보존해라.** 원문이 양가감정이나 불확실성을 담고 있다면 단순한 긍정·부정으로 평탄화하지 않는다. 원문에 없는 감정은 만들지 않는다.
-**"나"를 써라.** 1인칭이 비전문적인 게 아니다. "자꾸 생각나는 건..." 이나 "내가 좀 찝찝한 건..."이 진짜 사람이 생각하는 것처럼 보인다.
+**원문의 시점을 지켜라.** 원문이 1인칭이면 이를 불필요하게 제3자 서술로 바꾸지 않는다. 원문이 객관 서술이면 새로 "나"를 끼워 넣지 않는다.
-**좀 지저분해도 괜찮다.** 완벽한 구조는 알고리즘 냄새가 난다. 곁가지, 여담, 반쯤 정리된 생각은 사람답다.
+**필요 이상으로 매끈하게 만들지 마라.** 원문의 곁가지, 여담, 반쯤 정리된 생각이 문서 기능과 레지스터에 맞으면 기계적으로 정리하지 않는다. 새 곁가지나 친밀감은 만들지 않는다.
-**감정을 구체적으로.** "우려된다"가 아니라 "새벽 3시에 아무도 안 보는데 에이전트가 돌아가고 있다고 생각하면 좀 섬뜩하다."
+**감정은 원문에 있을 때만 구체적으로.** 원문이 감정을 표현한다면 모호한 관용구로 평탄화하지 않는다. 원문에 없는 감정이나 장면은 추가하지 않는다.
## 수정 전 (깨끗하지만 영혼 없음):
> 이 실험은 흥미로운 결과를 보여주었다. 에이전트가 300만 줄의 코드를 생성했다. 일부 개발자는 긍정적으로 반응했고 일부는 회의적이었다. 시사점은 아직 불분명하다.
@@ -51,19 +52,19 @@ The Korean guidance above is the reference — but Japanese voice has its own sp
- 接続詞が多すぎる——文ごとに「さらに」「また」「加えて」
- 読売新聞の社説かWikipediaの記事のように読める
-### Japanese voice injection:
+### Japanese voice guidance:
-**口語を使え。** 「この件はちょっとややこしい」は「本件は若干の複雑性を有する」より人間らしい。日本語の口語には終助詞(ね、よ、さ)、省略、倒置がある——AIはたいてい使わない。
+**文脈に合う口語を使え。** 原文または明示された Register がカジュアルな場合は、「この件はちょっとややこしい」のような自然な口語を保つ。学術・法務・技術文書を勝手に口語化しない。
-**具体的な数字とディテールを入れろ。** 「昨年の売上は3.2億円」は「著しい経済的成果を上げた」より100倍説得力がある。
+**具体的な数字とディテールを守れ。** 原文にある「昨年の売上は3.2億円」のような具体性を抽象語に置き換えない。原文にない数字やディテールは作らない。
**文の長短を交互に。** 長い文で背景とロジックを展開する。短い文で判断を下す。それだけ。
-**結論を出す勇気を持て。** 「このプランはダメだ」は「このプランにはある程度の限界が存在する可能性がある」よりずっと直接的。本物の人間は態度を持って書く。
+**原文の結論を弱めない。** 原文が「このプランはダメだ」と判断しているなら、過剰な留保で意味を薄めない。原文にない結論は加えない。
-**一人称を使え。** 「私は〜と思う」「私の知る限り」は「客観的な観点から見ると」より自然。すべての文章が客観中立を装う必要はない。
+**原文の人称を守れ。** 原文が一人称なら「私は〜と思う」を不自然な客観表現に変えない。客観文に新しい一人称を加えない。
-**不完全さを許容せよ。** 括弧での補足、ダッシュでの挿入、途中で方向転換——こうした「整っていない」部分こそ人間が書いた痕跡。
+**文脈上の不完全さを残せ。** 原文の括弧、挿入、方向転換が文書機能と Register に合うなら機械的に均さない。新しい脱線は作らない。
### Before (clean but soulless):
> この実験は注目すべき成果を生んだ。エージェントは300万行のコードを生成した。開発者の反応は肯定的なものと懐疑的なものに分かれた。その影響は現時点では不明である。
@@ -86,19 +87,19 @@ The Korean guidance above is the reference — but Chinese voice has its own spe
- 连接词过多,句句之间都有"此外"、"与此同时"、"不仅如此"
- 读起来像新华社通稿或百度百科词条
-### Chinese voice injection:
+### Chinese voice guidance:
-**用口语。** "这事儿不好办"比"此事存在较大难度"更像人写的。中文口语有大量儿化、语气词、省略结构——AI通常不敢用。
+**在合适的语境里保留口语。** 原文或明确的 Register 偏口语时,保留“这事儿不好办”这类自然表达;不要擅自把学术、法律或技术文档改成口语。
-**给具体数字和细节。** "去年营收3.2亿"比"取得了显著的经济效益"有说服力一百倍。
+**保留具体数字和细节。** 原文中的“去年营收3.2亿”不能被抽象概括替代,也不能凭空补充原文没有的数字或细节。
**句子长短交替。** 长句展开背景和逻辑。短句下判断。就这样。
-**敢下结论。** "这方案不行"比"这一方案在某些方面可能存在一定的局限性"直接得多。真人写东西是有态度的。
+**不要削弱原文的判断。** 原文明确说“这方案不行”时,不要用层层限定把结论稀释;原文没有的判断也不能新增。
-**用第一人称。** "我觉得"、"据我了解"比"从客观角度来看"更自然。不是所有文章都需要假装客观中立。
+**保持原文人称。** 原文用第一人称时,不要改成假装客观的表达;客观文本中也不要新增“我觉得”。
-**允许不完美。** 偶尔的括号补充、破折号插入、话说一半又转弯——这些"不工整"的地方恰恰是人写的痕迹。
+**保留符合语境的不规则感。** 原文中的括号补充、插入和转折若符合文档功能与 Register,就不要机械抹平;不要新增跑题内容。
### Before (clean but soulless):
> 该实验取得了令人瞩目的成果。智能代理生成了三百万行代码。开发者反应不一,部分表示认可,部分持保留态度。该技术的影响有待进一步观察。
@@ -121,19 +122,19 @@ The Korean guidance above is the reference — but English voice has its own spe
- Formal transitions that feel like a listicle ("Furthermore," "Moreover," "In addition")
- Opinions wrapped so many times in qualifiers they say nothing
-### English voice injection:
+### English voice guidance:
-**Use contractions.** "I don't know" reads human. "I do not know" reads like a legal brief. Real people contract — AI often doesn't unless told to.
+**Use contractions when the context permits them.** Keep or introduce contractions only when the source or an explicit casual Register supports them. Do not casualize academic, legal, or technical prose by default.
-**Vary sentence length radically.** Long sentences that build context and accumulate detail before the point lands. Then a short one. Just that.
+**Vary sentence length within the document's conventions.** Break metronomic repetition without turning formal prose into fragments or forcing a dramatic cadence.
-**Commit to opinions.** "This approach has merit" → "This actually works." Real writers don't pre-qualify every claim into meaninglessness.
+**Preserve the source's commitment.** If the source says "This actually works," do not pre-qualify it into meaninglessness. Do not add an opinion the source never expressed.
-**Use first person with genuine uncertainty.** "I'm not sure this is the right framing" lands more human than "Perspectives vary on this topic."
+**Preserve first person and genuine uncertainty when present.** Do not flatten "I'm not sure this is the right framing" into detached boilerplate, and do not add a first-person stance to an objective source.
-**Break register deliberately.** Build an argument, then parenthetically admit something doesn't fit — or drop a dry observation before resuming. That friction is human.
+**Vary rhythm without mixing registers.** Parenthetical friction or a dry observation may survive when it belongs to the source voice, but every sentence must remain coherent with the document's dominant or explicitly requested Register.
-**Let a sentence fragment stand.** For emphasis. Like that.
+**Let an existing sentence fragment stand when the context supports it.** Do not manufacture fragments in formal prose merely to sound human.
**Use idiom naturally — but not the AI-overused ones.** "The math doesn't add up," "missing the forest for the trees," "a moving target" are fine. Avoid the filler idioms that Pattern #22 covers.
@@ -145,19 +146,3 @@ The Korean guidance above is the reference — but English voice has its own spe
---
-## Per-Tone Voice Notes (v3.10)
-
-The six v1 tones each pull different levers from the guidance above. Use `--tone ` to activate.
-
-Coverage note: the tone table below covers all six named tones for ko/en. zh/ja currently use the language-specific voice guidance above and fall back to profile-only mode for named tones, so separate 6-tone zh/ja voice notes are not defined yet.
-
-| Tone | Language coverage | Voice lever emphasis | Profile backbone |
-|------|-------------------|----------------------|-----------------|
-| `casual` | ko/en; zh/ja gap noted above | Contractions, first-person, opinions, humor, messiness | `blog` / `social` |
-| `professional` | ko/en; zh/ja gap noted above | Clarity and concision; formal register but not stiff; suppress filler | `email` / `formal` (legal/medical force fidelity floor 0.65) |
-| `academic` | ko/en; zh/ja gap noted above | Objective tone, evidence references, suppress first-person and emoticons | `academic` / `technical` |
-| `narrative` | ko/en; zh/ja gap noted above | First-person anchor, scene detail, emotional presence, time flow — see `profiles/narrative.md` | `profiles/narrative.md` (new) |
-| `marketing` | ko/en; zh/ja gap noted above | Short impact sentences, persuasive verbs, CTA-friendly; emoticons allowed | `marketing` |
-| `instructional` | ko/en; zh/ja gap noted above | Imperative verbs at sentence head, numbered structure preserved, hedging suppressed — see `profiles/instructional.md` | `profiles/instructional.md` (new) |
-
-Deeper voice guidance for each tone lives in the corresponding profile file. This table is a pointer only.
diff --git a/docs/API.md b/docs/API.md
index 99f12ff..800243e 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -40,19 +40,19 @@ console.log(result.interpretation); // mostly human
## Persona CLI commands
-Personas are the reusable voice-composition unit. The YAML frontmatter is the
-deterministic single source of truth; the Markdown body is docs-only and is
-never sent to the model. Custom personas live in `custom/personas//` and
-shadow same-id built-ins under `personas//`. Every write and edit passes
-the persona safety gate (`validatePersona`), which clamps the MPS/fidelity
-floors to their core minimum and rejects gate-weakening keys.
+Personas are optional, reusable voice fingerprints. Omitting `--persona`
+preserves the source voice. Persona v2 frontmatter is the deterministic single
+source of truth; the Markdown body is docs-only and is never sent to the model.
+Custom personas live in `custom/personas//` and shadow same-id built-ins
+under `personas//`. Validation rejects document policy, register,
+verification, and meaning-preservation fields: those belong to separate axes.
| Command | Description |
| --- | --- |
-| `patina persona new ` | Author a custom persona (`--from-sample `, `--describe ""`, `--template`, or an interactive wizard). |
-| `patina persona list` | List built-in and custom personas (`--lang`, `--format json`). |
-| `patina persona show ` | Print a persona's normalized config — id, name, lang, depth, MPS/fidelity floors, active blocks, `target_features` keys, resolved path, and source. `--json` emits the normalized object. The docs-only body is never printed. |
-| `patina persona rm ` | Remove a custom persona. Built-in library seeds and the `preserve` default are protected. Requires `--force` or an interactive y/N confirm; only files under `custom/personas//` are ever deleted. |
+| `patina persona new ` | Author a custom Persona (`--from-sample `, `--describe ""`, `--template`, or an interactive wizard). |
+| `patina persona list` | List built-in and custom Personas (`--lang`, `--format json`). |
+| `patina persona show ` | Print a Persona's normalized voice config: id, name, language, active blocks, `target_features` keys, resolved path, and source. `--json` emits the normalized object. The docs-only body is never printed. |
+| `patina persona rm ` | Remove a custom Persona. Built-in library Personas are protected. Requires `--force` or an interactive y/N confirm; only files under `custom/personas//` are ever deleted. |
| `patina persona edit ` | Copy-on-edit into `custom/personas//`. Editing a built-in copies it into custom (a shadow), preserving the library. Re-derive the voice with `--from-sample ` / `--describe ""`, or keep it and rename with `--name ""`. |
Common options: `--lang ` (default `ko`), and `--backend ` for
@@ -229,8 +229,8 @@ XLIFF target-language (cached), not the global config language.
Over-editing guard (Study 1 RQ5b): rewriting text that already reads human
@@ -250,8 +250,11 @@ cannot silently override a pinned config (reproducible CI runs).
Omitting the option preserves the source document's dominant register. There
+is no auto mode: source-preserving behavior is the default and avoids a
+second, model-dependent inference path.
Create a user-input error that should exit with code 2.
@@ -284,17 +287,17 @@ user or pro packs in custom/patterns/{lang}-.md. On a filename collision
the custom pack wins (same precedence the persona and lexicon loaders give
custom/), so an installed pack can also override a built-in one.
Strip the individual pattern sections a profile marks suppress from loaded
-pattern packs, so the rewrite/audit/score prompt never carries those rules.
-
pattern-overrides in a profile's frontmatter is keyed by language then
-numeric pattern id, with action suppress or reduce. v1 honors suppress
-deterministically (the LLM cannot flag a rule it was never given); reduce
-has no weight knob yet and is intentionally left in place. Packs without a
-matching override are returned unchanged (same object identity).
Apply a document type's deterministic pattern policy.
+
suppress removes the pattern definition before any prompt is built.
+reduce and amplify remain in the structured policy passed to the model;
+the deterministic layer does not invent unsupported numeric weights.
@@ -342,7 +345,7 @@ is a hint surface, not a verdict.
Split a built prompt into a cacheable static prefix and a dynamic tail for
provider prompt caching. The prefix is everything before the FIRST input
fence: on a first-turn prompt that is the full static catalog (identical
-across requests for a given lang/profile/persona), while refine prompts
+across requests for a given language/document-type/persona), while refine prompts
carry variable fenced references near the top, so their prefix falls under
the minimum and caching is skipped — avoiding cache writes that would never
be re-read.
@@ -414,8 +417,8 @@ single prompt can never carry two contradictory contracts (issue #397).
Check whether a hostname is localhost or loopback.
@@ -840,23 +843,23 @@ Create a SIGINT-aware cancellation controller for long-running CLI operations.
const cancellation = createCancellationController();
cancellation.install();
```
-
+
-## resolveProfileForLanguage(profileName, lang, [logger]) ⇒ string
-Resolve a profile name against language-specific profile limits.
+## resolveDocumentTypeForLanguage(documentTypeName, lang, [logger]) ⇒ string
+Resolve a document type against language-specific policy limits.
**Kind**: global function
-**Returns**: string - Effective profile name.
+**Returns**: string - Effective document type.
| Param | Type | Description |
| --- | --- | --- |
-| profileName | string | Requested profile name. |
+| documentTypeName | string | Requested document type. |
| lang | string | Active language code. |
| [logger] | object | Logger with warn(event, payload). |
**Example**
```js
-resolveProfileForLanguage('namuwiki', 'en') // 'default'
+resolveDocumentTypeForLanguage('namuwiki', 'en') // 'default'
```
@@ -907,28 +910,31 @@ Return the repository root inferred from this source file location.
```js
const root = getRepoRoot();
```
-
+
-## resolveTone(options) ⇒ Object
-Resolve CLI/config tone settings into prompt-ready tone metadata.
+## resolveRegister(options) ⇒ object \| null
+Resolve an explicit register override.
+
+Omitting the option preserves the source document's dominant register. There
+is no `auto` mode: source-preserving behavior is the default and avoids a
+second, model-dependent inference path.
**Kind**: global function
-**Returns**: Object - Tone metadata.
+**Returns**: object \| null - Prompt-ready register metadata, or null when omitted.
**Throws**:
-- Error When cliTone or configTone is not supported.
+- Error When either value is unsupported.
| Param | Type | Description |
| --- | --- | --- |
-| options | object | Tone inputs. |
-| [options.cliTone] | string \| null | CLI tone override. |
-| [options.configTone] | string \| null | Configured tone value. |
-| [options.lang] | string | Active language code. |
+| options | object | Register inputs. |
+| [options.cliRegister] | string \| null | CLI register override. |
+| [options.configRegister] | string \| null | Configured register value. |
**Example**
```js
-const tone = resolveTone({ cliTone: 'casual', lang: 'ko' });
+const register = resolveRegister({ cliRegister: 'casual' });
```
@@ -1083,52 +1089,49 @@ custom/), so an installed pack can also override a built-in one.
```js
const patterns = loadPatterns(getRepoRoot(), 'en');
```
-
+
+
+## loadDocumentType(repoRoot, documentTypeName) ⇒ Object
+Load a named document type. A custom policy at
+custom/document-types/{name}.md shadows the built-in document-types/{name}.md.
-## loadProfile(repoRoot, profileName) ⇒ Object
-Load a named profile from profiles/{profileName}.md after path validation.
+The Markdown body is explanatory documentation only. Runtime policy comes
+from validated structured frontmatter.
**Kind**: global function
-**Returns**: Object - Parsed profile document.
+**Returns**: Object - Parsed and validated policy document.
**Throws**:
-- Error When the profile name is invalid or the file cannot be read.
+- Error When the name is invalid or the file cannot be read.
| Param | Type | Description |
| --- | --- | --- |
| repoRoot | string | Repository root path. |
-| profileName | string | Profile file stem. |
+| documentTypeName | string | Document-type file stem. |
**Example**
```js
-const profile = loadProfile(getRepoRoot(), 'default');
+const documentType = loadDocumentType(getRepoRoot(), 'technical');
```
-
+
-## applyProfilePatternOverrides(packs, profile, lang) ⇒ Array.<{body: string}>
-Strip the individual pattern sections a profile marks `suppress` from loaded
-pattern packs, so the rewrite/audit/score prompt never carries those rules.
+## applyDocumentTypePatternPolicy(packs, documentType, lang) ⇒ Array.<{body: string}>
+Apply a document type's deterministic pattern policy.
-`pattern-overrides` in a profile's frontmatter is keyed by language then
-numeric pattern id, with action `suppress` or `reduce`. v1 honors `suppress`
-deterministically (the LLM cannot flag a rule it was never given); `reduce`
-has no weight knob yet and is intentionally left in place. Packs without a
-matching override are returned unchanged (same object identity).
+`suppress` removes the pattern definition before any prompt is built.
+`reduce` and `amplify` remain in the structured policy passed to the model;
+the deterministic layer does not invent unsupported numeric weights.
**Kind**: global function
**Returns**: Array.<{body: string}> - Packs with suppressed sections removed.
| Param | Type | Description |
| --- | --- | --- |
-| packs | Array.<{body: string}> | Loaded pattern packs from loadPatterns. |
-| profile | Object \| null | Loaded profile (loadProfile). |
+| packs | Array.<{body: string}> | Loaded pattern packs. |
+| documentType | Object \| null | Loaded document type. |
| lang | string | Active language code. |
-**Example**
-```js
-const packs = applyProfilePatternOverrides(loadPatterns(root, 'ko'), loadProfile(root, 'legal'), 'ko');
-```
## loadCoreFile(repoRoot, filename) ⇒ Object
@@ -1213,7 +1216,7 @@ Format a raw backend result for CLI output mode and requested format.
**Returns**: string - User-facing formatted output.
**Throws**:
-- TypeError When `result` or `opts.tone` carries values JSON.stringify cannot serialize (circular references, BigInt) — the json format serializes the result payload, and the tone footer serializes `opts.tone.tone_evidence`.
+- TypeError When JSON output carries unserializable values.
| Param | Type | Default | Description |
@@ -1222,11 +1225,11 @@ Format a raw backend result for CLI output mode and requested format.
| mode | string | | Output mode: rewrite, diff, audit, or score. |
| [parsed] | object | {} | Parsed CLI options. |
| [opts] | object | {} | Formatting options. |
-| [opts.tone] | object \| null | | Tone metadata to append. |
+| [opts.register] | object \| null | | Explicit register metadata. |
| [opts.logger] | object | | Logger for output warnings. |
| [opts.env] | object | | Environment map for color decisions. |
| [opts.stdout] | object | | Stdout-like stream for color decisions. |
-| [opts.auditBackstop] | string | | Deterministic audit-mode section to append before the tone footer. |
+| [opts.auditBackstop] | string | | Deterministic audit-mode section. |
| [opts.persona] | object \| null | | Persona metadata to append. |
**Example**
@@ -1359,7 +1362,7 @@ is a hint surface, not a verdict.
Split a built prompt into a cacheable static prefix and a dynamic tail for
provider prompt caching. The prefix is everything before the FIRST input
fence: on a first-turn prompt that is the full static catalog (identical
-across requests for a given lang/profile/persona), while refine prompts
+across requests for a given language/document-type/persona), while refine prompts
carry variable fenced references near the top, so their prefix falls under
the minimum and caching is skipped — avoiding cache writes that would never
be re-read.
@@ -1415,7 +1418,7 @@ Build the LLM prompt for rewrite, diff, audit, or score mode.
**Returns**: string - Complete prompt text.
**Throws**:
-- TypeError When `options.tone.tone_evidence` contains values JSON.stringify cannot serialize (circular references, BigInt).
+- TypeError When register evidence cannot be JSON-serialized.
| Param | Type | Default | Description |
@@ -1423,13 +1426,14 @@ Build the LLM prompt for rewrite, diff, audit, or score mode.
| options | object | | Prompt inputs. |
| options.config | object | | Effective patina config. |
| options.patterns | Array.<object> | | Loaded pattern packs. |
-| options.profile | object \| null | | Parsed profile document. |
-| options.voice | object \| null | | Parsed voice guide. |
-| [options.persona] | object \| null | | Optional validated persona payload. |
+| options.documentType | object \| null | | Parsed document-type policy. |
+| options.voice | object \| null | | Parsed claim-safe voice baseline. |
+| [options.persona] | object \| null | | Optional validated voice persona. |
| options.scoring | object \| null | | Parsed scoring guide. |
| options.text | string | | Input text. |
| [options.mode] | string | "rewrite" | Output mode. |
-| [options.tone] | object \| null | | Tone resolution metadata. |
+| [options.register] | object \| null | | Explicit register metadata. |
+| [options.promptMode] | 'strict' \| 'minimal' | strict | Prompt catalog detail level. |
| [options.documentSignals] | Array.<string> \| null | | Deterministic document measurements (e.g. dominant Korean register) injected into rewrite prompts as ground truth for the Phase 0 document brief. |
| [options.includeSelfAudit] | boolean | true | Include the Phase 3 self-audit in rewrite instructions; the rewrite loop passes false to skip the token cost (#444). |
| [options.jargon] | string | "keep" | Technical-term policy (keep|explain|remove); non-default values add the opt-in transformation directive to rewrite prompts. |
@@ -1437,7 +1441,7 @@ Build the LLM prompt for rewrite, diff, audit, or score mode.
**Example**
```js
-const prompt = buildPrompt({ config, patterns, profile, voice, scoring, text: 'Draft' });
+const prompt = buildPrompt({ config, patterns, documentType, voice, scoring, text: 'Draft' });
```
@@ -1741,7 +1745,7 @@ Score fidelity between original and rewritten text using length plus LLM criteri
**Example**
```js
-const fidelity = await scoreFidelity({ original: 'A', rewritten: 'A', callLLM: async () => '{"criteria":{"meaning":3,"tone":3,"no_unintended_additions":3}}' });
+const fidelity = await scoreFidelity({ original: 'A', rewritten: 'A', callLLM: async () => '{"claims_preserved":3,"no_fabrication":3,"audience_register_match":3}' });
```
@@ -1772,32 +1776,32 @@ Combine AI-likeness, inverted fidelity, and optional deterministic score.
| options | object | Combined score inputs. |
| options.aiLikeness | number | AI-likeness score, lower is better. |
| options.fidelity | number | Fidelity score, higher is better. |
-| [options.profile] | string | Profile name for configured weights. |
+| [options.documentType] | string | Document type for configured weights. |
| [options.config] | object | Effective config. |
| [options.deterministicScore] | number \| object \| null | Optional deterministic score. |
**Example**
```js
-const score = combinedScore({ aiLikeness: 20, fidelity: 90, profile: 'default', config: {} });
+const score = combinedScore({ aiLikeness: 20, fidelity: 90, documentType: 'default', config: {} });
```
-
+
-## validateProfileName(name) ⇒ void
-Validate a profile name before resolving profiles/{name}.md.
+## validateDocumentTypeName(name) ⇒ void
+Validate a document-type name before resolving document-types/{name}.md.
**Kind**: global function
**Throws**:
-- PatinaCliError When the name is empty, non-string, or contains unsafe characters.
+- PatinaCliError When the name is empty, non-string, or unsafe.
| Param | Type | Description |
| --- | --- | --- |
-| name | string | Profile name supplied by CLI or config. |
+| name | string | Name supplied by CLI or config. |
**Example**
```js
-validateProfileName('default');
+validateDocumentTypeName('technical');
```
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 63d3c2e..61b73d1 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -5,10 +5,14 @@ with a single rule. This document is the **canonical contract** for which method
governs each surface, which module belongs to which lane, and the invariants each
lane must uphold.
-It is the stable boundary the persona hardening line was built on — the enforcing
-safety gate, multilingual personas, the register/profile precedence, and
-custom-voice authoring ([ROADMAP](ROADMAP.md) Product Phase 4, now shipped) — and
-that hosted enhancement and further persona-gate work continue to build on.
+The v7 boundary also defines three independent rewrite axes: Document Type owns
+document policy, Persona v2 optionally owns reusable voice, and Register owns
+casual/professional delivery. None is inferred from another.
+This is field ownership, not three competing full-text styles. Meaning and
+safety are the outer invariant; Document Type resolves structure and domain
+constraints, Persona resolves idiolect and rhythm, and Register resolves only
+casual/professional markers. No active axis supplies a missing axis.
+
See also: [`CONTRIBUTING.md`](../CONTRIBUTING.md) (the determinism rule —
"Adding a Deterministic Detection Signal"), [`docs/HARNESS.md`](HARNESS.md) (the
@@ -26,12 +30,12 @@ key, fully reproducible. Lives in `src/features/*` and the deterministic
backstops. This is patina's **trust / auditability substrate**, the public,
offline, no-key surface, and the ground truth that the benchmark/CI layer pins.
-### Method P — persona / LLM (transform with a model, prove meaning survived)
+### Method P — LLM transformation (transform with a model, prove meaning survived)
Produces its answer by prompting an LLM — to rewrite, or to narrate a score /
-audit / diff. A persona optionally shapes the *voice* of a rewrite. Method P may
-change wording but MUST NOT change the underlying claim, numbers, polarity, or
-causation, and MUST prove the meaning survived.
+audit / diff. Document Type, Persona, and Register may independently shape a
+rewrite. Method P may change wording but MUST NOT change the underlying claim,
+numbers, polarity, or causation.
### The binding rule
@@ -60,13 +64,12 @@ Method-D anchor* under each surface — see [Known seams](#known-seams).
**Lane B (Method P) MUST:**
- anchor every shipped output to a Method-D computation (reconcile, backstop, or
gate).
-- enforce the meaning-preservation floors (MPS ≥ 70, fidelity ≥ 70) and never
- weaken them. A persona may *raise* a floor, never lower it
- (`src/personas/schema.js`).
-- treat a persona as **voice composition only**: it may reweight emphasis /
- coverage and style, but never inject claims, numbers, examples,
- metaphors-as-facts, or worldview (`blocks.worldview` is schema-reserved and
- inactive in v1).
+- enforce global meaning preservation independently of every rewrite axis.
+ `--verify` owns the configurable MPS/fidelity floors and retry path; no
+ Document Type, Persona, or Register may weaken them.
+- treat Persona v2 as **voice composition only**. It may shape vocabulary,
+ explanation habits, rhythm, and other voice targets, but never document
+ policy, register, claims, safety thresholds, or worldview.
- keep its own deterministic assets (`src/features/persona-match.js`,
`src/verify.js#deterministicMeaningGuard`) auditable and LLM-free even though
they serve Lane B.
@@ -82,25 +85,22 @@ Method-D anchor* under each surface — see [Known seams](#known-seams).
## Surface → method → Method-D anchor
-Every CLI mode prompts the backend; the rightmost column is what keeps it
-auditable.
+Backend-backed modes use Method P; the rightmost column is the Method-D anchor.
| Surface | mode | LLM call? | Method-D anchor |
|---|---|---|---|
-| default | `rewrite` | yes | `deterministicMeaningGuard` (dropped-numbers, always); persona **safety gate** MPS/fidelity/churn→numbers (if `--persona`); `verify.js` MPS/fidelity + retry (if `--verify`) |
-| `--audit` | `audit` | yes | `buildDeterministicAuditBackstop` (deterministic detections appended) |
-| `--score` | `score` | yes | `withDeterministicScore` reconciles the LLM overall with deterministic signals; `--exit-on` score gate |
+| default | `rewrite` | yes | `deterministicMeaningGuard`; optional Persona match/churn advisory; `verify.js` MPS/fidelity + retry only with `--verify` |
+| `--audit` | `audit` | yes | `buildDeterministicAuditBackstop` |
+| `--score` | `score` | yes | `withDeterministicScore`; optional `--exit-on` gate |
+| `--score --offline` | `score` | **no** | deterministic signal score |
| `--diff` | `diff` | yes | deterministic pattern/detection report |
-| `--preview [--serve]` | preview job | yes | deterministic prose extraction + word-diff rendering over the rewrite |
-| `patina-score` (bin) | — | **no** | pure Method D: hot-paragraph ratio over `analyzeText()` — the deterministic CI gate (`scripts/prose-score.mjs`) |
-| playground / hosted **rewrite** | — | yes | rewrite-first (no offline browser audit surface); reuses prompt + scoring assets server-side |
+| `--preview [--serve]` | preview job | yes | deterministic prose extraction + word-diff rendering |
+| `patina-score` (bin) | — | **no** | hot-paragraph ratio over `analyzeText()` |
+| playground / hosted rewrite | — | yes | shared server-side prompt, analysis, and scoring assets |
-Notes: `--persona` runs on **rewrite, non-preview**, in any supported language
-(ko/en/zh/ja); ko applies the `preserve` default implicitly, en/zh/ja are opt-in
-(Lane B only; `src/cli/run.js#resolvePersonaForRun`). The safety gate enforces
-MPS/fidelity/dropped-numbers and reuses `--verify`'s real scores when present;
-churn and persona-match are advisory. `--serve` is a `--preview` transport
-option, not a standalone mode.
+Notes: Persona is opt-in for rewrite/preview in ko/en/zh/ja. Omission preserves
+the source voice. Persona match and churn are advisory; meaning and number
+checks remain global. `--serve` is a `--preview` transport option.
---
@@ -125,17 +125,15 @@ option, not a standalone mode.
- `src/features/persona-match.js` — LLM-free persona-match scorer. It lives in
`features/` **on purpose**, to inherit the determinism guarantee, but it is
- authored for Lane B's persona gate. This is the one piece of Lane B's
- verification that is already deterministic.
+ authored for Lane B's optional Persona quality report.
-### Lane B — persona / LLM rewrite (LLM-backed)
+### Lane B — LLM rewrite and optional Persona voice (LLM-backed)
-- `src/personas/{schema,loader,compose,gates}.js` — persona config SSOT, loader,
- localized prompt directive, gate evaluation (enforcing safety vs advisory);
- `personas/{ko,en,zh,ja}/*.md` — built-in personas (each language ships at least
- `preserve`); `custom/personas/{lang}/*.md` — user-authored personas
-- `src/commands/persona.js` — `patina persona new|list` custom voice authoring
- (one-time LLM draft + deterministic anchors → validated persona file)
+- `src/personas/{schema,loader,compose,gates}.js` — Persona v2 voice schema,
+ loader, localized prompt directive, and advisory voice-quality evaluation;
+ `personas/{ko,en,zh,ja}/*.md` — built-in Personas;
+ `custom/personas/{lang}/*.md` — user-authored Personas
+- `src/commands/persona.js` — `patina persona new|list|show|edit|rm`
- `src/prompt-builder.js` — rewrite/score/audit/diff prompt construction
- `src/scoring.js` — LLM MPS/fidelity scoring (excluded from the deterministic
benchmark/gate layer)
@@ -156,8 +154,9 @@ option, not a standalone mode.
kept as shared transport)
- `src/auth.js`, `commands/auth.js`, `commands/doctor.js`
- `src/ocr.js` — image → text input extraction
-- `scoring` and `verification` are separate configuration namespaces; persona
- thresholds remain in persona definitions.
+- `scoring`, `verification`, and `personas.thresholds` are separate
+ configuration namespaces. Persona thresholds cover advisory voice quality;
+ verification owns MPS/fidelity floors.
### Packaged research comparator (unsupported)
@@ -167,60 +166,21 @@ option, not a standalone mode.
## Seams: resolved and remaining
-Boundaries where the lanes used to bleed. The persona hardening line closed most
-of them; what remains is named here as the surface for later work.
-
-### Resolved
-
-1. **The persona gate now enforces safety.** The gate is split into an *enforcing*
- safety decision (MPS/fidelity when evaluated + the deterministic
- dropped-numbers guard) and *advisory* signals (churn, persona-match). A safety
- failure sets a non-zero exit (4), non-destructively — output is still emitted.
- Churn and persona-match warn but never block (`personas/gates.js`,
- `run.js` persona block).
-2. **Lane B reuses `verify.js`'s real scores.** When `--verify` runs, the persona
- safety gate consumes its scored MPS/fidelity instead of the backend's
- self-reported JSON; without `--verify` it falls back to the deterministic
- backstop (dropped-numbers + advisory churn), never silently passing an
- unmeasured floor.
-3. **The two verification paths share one decision.** `verify.js` and the persona
- safety gate no longer score independently — the gate consumes verify's scores
- (single path when both are active).
-4. **Churn reclassified from safety to advisory.** Live calibration showed
- legitimate KO humanizing rewrites churn ~0.5–0.85 while preserving meaning, so
- surface churn is not a meaning signal; it warns only. `mps_floor`/`fidelity_floor`
- stay core (70, enforcing); `churn_max`/`persona_match_min` are observation-
- informed advisory thresholds. `source` stays `placeholder` **deliberately** —
- these are advisory-only signals, so a formal 2-round promotion is not required
- to ship them. Promotion trigger: only if `churn_max` or `persona_match_min`
- (or the meaning-proxy) is ever made **enforcing**. Promotion procedure: the
- 2-round ablation (`aggregateAblation`/`ablationDecision` in `src/personas/gates.js`,
- driven by `scripts/persona-ablation.mjs`) proving ~0 false positives on
- legitimate rewrites, after which `source` records `calibrated`. Until then the
- placeholder value is the honest provenance for an advisory threshold.
-5. **Personas are multilingual and are the sole voice owner.** `--persona` runs on
- ko/en/zh/ja. As of v6.2 the persona owns ALL voice: whenever a persona is
- active (including the `preserve` default), the profile contributes only its
- pattern policy and its voice body is not sent to the model. Profiles were
- reduced to a pattern-policy-only axis (`voice-overrides` frontmatter removed,
- voice-guidance bodies dropped, versions bumped); a runtime migration warning
- fires when a non-default profile is used for a rewrite without a voice-owning
- persona. Register precedence stays `--tone` > persona > profile. (persona
- schema still forbids pattern control — that half stays.)
-
-6. **Deterministic meaning-floor proxy shipped (advisory).** `src/features/meaning-proxy.js`
- (Lane A, LLM-free by construction — enforced by a module-boundary test)
- provides `evaluateMeaningProxy({original, rewrite, lang})` as a conjunction of
- high-precision invariants: dropped numbers, rare-content-token recall (active
- only with ≥3 rare tokens; recall <0.5 warn / <0.3 fail), negation-polarity
- delta (word/token-boundary, not raw substring), and length-ratio extremes
- ([0.4, 2.5]). It rides the persona report JSON (`meaning_proxy`) and the gate's
- advisory list. **Phase A is advisory-only** (no CLI warning, no exit change);
- dropped numbers stay separately enforced. **Phase B promotion** to enforcing
- (exit 4) requires the formal 2-round ablation (`aggregateAblation`/`ablationDecision`)
- proving ~0 false positives on legitimate rewrites (live-quality + dogfood +
- the local calibration corpus) and true positives on the meaning-broken
- fixtures, recording `source: calibrated`.
+### Resolved axis ownership
+
+1. **Document Type owns document policy.** `document-types/*.md` supplies genre,
+ purpose, structural conventions, and `pattern-overrides`; it contributes no
+ voice or Register instruction.
+2. **Persona v2 owns reusable voice only.** Persona is optional in every
+ supported language. Its schema rejects Document Type, Register, pattern
+ policy, verification, meaning floors, and rewrite-depth fields.
+3. **Register owns delivery only.** `--register` and `register:` accept exactly
+ `casual` or `professional`. Omission preserves the source register.
+4. **Safety is global.** The deterministic meaning guard applies regardless of
+ axis selection; `--verify` owns MPS/fidelity floors and one conservative
+ retry. Persona match and surface churn remain advisory.
+5. **The v7 cutover is explicit.** `profile`, `tone`, and `formality` inputs
+ fail with migration errors rather than aliases or silent fallback.
### Remaining
diff --git a/docs/CLI.md b/docs/CLI.md
index 7b38e42..51df4e3 100644
--- a/docs/CLI.md
+++ b/docs/CLI.md
@@ -25,7 +25,9 @@ patina --lang en --score --exit-on 30 draft.md
## Output formats
-`--format markdown` is the default and preserves the existing human-readable output. `--format text` emits the same user-facing content without the YAML tone footer. `--format json` wraps every mode in a stable envelope:
+`--format markdown` is the default human-readable output. `--format text` emits
+the same user-facing content without metadata. `--format json` wraps every mode
+in a stable envelope:
```json
{
@@ -33,9 +35,10 @@ patina --lang en --score --exit-on 30 draft.md
"format": "json",
"overall": 23,
"categories": [],
- "tone": { "tone": null, "tone_source": "profile_only" },
+ "register": null,
"mps": null,
"gateResult": { "threshold": 30, "overall": 23, "passed": true, "exitCode": 0 },
+ "persona": null,
"output": "raw model output after patina cleanup"
}
```
@@ -58,67 +61,102 @@ patina --verify --lang ko --backend codex-cli draft.md
- It is a rewrite modifier, not a separate mode: combining it with `--score`, `--audit`, `--diff`, or `--preview` is an input error (those do not rewrite).
- The MPS/fidelity scorers run through the **selected backend**, so `--verify` works with HTTP and local CLI backends alike. It adds up to four extra model calls (two scorers, plus a retry that re-scores), so the plain rewrite stays the fast/cheap default.
-The Node CLI keeps `scoring` and `verification` separate; `--verify` uses
-`verification.{mps-floor,fidelity-floor}`, while persona thresholds remain in
-persona definitions.
+The Node CLI keeps Persona quality and verification separate. `--verify` uses
+`verification.{mps-floor,fidelity-floor}`; `personas.thresholds` contains only
+advisory voice-match and surface-churn thresholds.
### Deterministic meaning guard (always on, no LLM)
Every rewrite (with or without `--verify`) runs a cheap deterministic guard that warns on stderr when numbers present in the source go missing from the rewrite. It never blocks output and makes no model calls (length is intentionally not checked — a humanizer legitimately changes length).
-## Korean persona rewrite: `--persona`
+## Three independent rewrite axes
-`--persona ` selects a validated Korean persona from `personas/ko` (or a same-id custom persona) for the rewrite harness. With no explicit persona, Korean rewrite mode uses the conservative `preserve` persona: style-only, minimal change, and MPS/fidelity hard floors still enforced.
+These options compose but never imply one another:
-v1 KO seed library (`personas/ko`): `preserve` (default), `blog-essay`, `pragmatic-founder`, `technical-explainer`, `soft-professional`, and `natural-ko` — a cleanup persona that strips AI-tell register (wellness-translationese, flattery, hype vocabulary) into plain Korean while preserving claims.
+| Axis | Input | Runtime asset | Omission |
+|---|---|---|---|
+| Document Type | `--document-type ` / `document-type:` | `document-types/.md` or `custom/document-types/.md` | `default` document policy |
+| Persona | `--persona ` / `persona:` | `personas//.md` or a custom Persona | preserve source voice |
+| Register | `--register casual|professional` / `register:` | delivery directive | preserve source register |
+
+Document Type controls purpose, audience, structure, style, avoidance rules, and
+`pattern-overrides`; Persona controls reusable voice; Register controls only
+casual/professional delivery. None may change claims, numbers, polarity,
+causation, or verification floors. The removed v6 inputs `--profile`, `--tone`,
+and `--formality` are rejected rather than aliased.
+Meaning and safety remain above all three axes. Apparent conflicts resolve by
+field ownership rather than by letting one axis override another wholesale:
+Document Type owns structure and domain constraints, Persona owns idiolect and
+rhythm, and Register owns casual/professional markers. Explicit axes do not
+populate omitted axes.
+
+
+## Optional voice Persona: `--persona`
+
+`--persona ` selects a validated Persona v2 voice fingerprint from
+`personas//` or a same-id custom Persona. Omitting the option preserves
+the source voice in every language.
+
+Built-in libraries:
+
+- ko: `blog-essay`, `natural-ko`, `pragmatic-founder`, `soft-professional`,
+ `technical-explainer`
+- en: `blog-essay`, `natural-en`, `technical-explainer`
+- zh: `blog-essay`, `natural-zh`
+- ja: `blog-essay`, `natural-ja`
```bash
-patina --persona preserve draft.md
patina --lang ko --persona pragmatic-founder draft.md
+patina --lang en --persona natural-en draft.md
```
Contract:
-| Combination | v1 behavior | Reason |
-|---|---|---|
-| `patina file` | allowed; equivalent to `persona=preserve` in Korean rewrite | safe default |
-| `--persona p file` | allowed | core v1 surface |
-| `--lang ko --persona p` | allowed | KO library only |
-| `--lang en|zh|ja --persona p` | input error | no non-KO persona library |
-| `--score/--audit/--diff --persona p` | input error | persona v1 is rewrite-only |
-| `--preview --persona p` | input error | preview migration is later |
-| `--persona p --jargon x,y`, `--tone a,b` | input error | comma-list variants are preview-only |
-| `--persona p --tone casual` | allowed as compatibility hint | persona remains outer contract |
-| `--persona p --profile blog` | allowed as compatibility hint | legacy profile remains non-authoritative |
-| `--persona p --jargon explain|remove` | input error | terminology rewrite is not gated in v1 |
-
-Persona files are frontmatter-only at runtime. Markdown bodies are documentation and never enter prompts. The `worldview` block is reserved but inactive in v1. Even `depth: content` personas may adjust emphasis and coverage only; they cannot invent claims or make MPS/fidelity advisory.
-
-For `--format json`, rewrite output includes a `persona` field when a persona gate ran:
+| Combination | v2 behavior |
+|---|---|
+| `patina file` | no Persona; preserve source voice |
+| `--persona p file` | apply only Persona `p`'s voice blocks |
+| `--lang ko|en|zh|ja --persona p` | use that language's Persona library |
+| `--score/--audit/--diff --persona p` | input error; Persona is rewrite-only |
+| `--preview --persona p` | allowed |
+| `--persona p --register casual|professional` | allowed; independent axes |
+| `--persona p --document-type blog` | allowed; independent axes |
+| `--persona p --jargon explain|remove` | allowed in rewrite/preview |
+
+Persona v2 files are frontmatter-only at runtime. Markdown bodies are
+documentation and never enter prompts. Persona fields may define vocabulary,
+metaphor preferences, explanation habits, sentence structure, and measurable
+voice targets. They may not define Document Type, Register, pattern policy,
+verification, MPS/fidelity floors, or rewrite depth.
+
+For `--format json`, rewrite output includes advisory voice-quality metadata
+only when a Persona is active:
```json
{
"persona": {
- "id": "preserve",
- "depth": "style-only",
+ "id": "natural-en",
"thresholds_source": "placeholder",
"match": 82.4,
- "mps": 91,
- "fidelity": 88,
"over_edit_churn": 0.18,
- "gate_result": { "pass": true, "hardFailures": [] }
+ "gate_result": {
+ "pass": true,
+ "hardFailures": [],
+ "safetyFailures": [],
+ "advisory": []
+ }
}
}
```
## Transformations beyond cleanup: `--jargon`
-By default patina is a conservative humanizer: it removes AI tells without changing a sentence's claim or framing. `--jargon` is an explicit opt-in for adjusting terminology for a different audience. It applies to the default rewrite and `--preview` only; combining it with `--score`, `--audit`, or `--diff` is an input error (those modes do not rewrite). A full voice/register change is `--persona` / `--tone`, not a rewrite depth.
+By default patina is a conservative humanizer: it removes AI tells without changing a sentence's claim or framing. `--jargon` is an explicit opt-in for adjusting terminology for a different audience. It applies to the default rewrite and `--preview` only; combining it with `--score`, `--audit`, or `--diff` is an input error (those modes do not rewrite). A voice or delivery override uses `--persona` or `--register`; neither changes rewrite depth.
```bash
patina --jargon remove draft.md # de-jargonized rewrite
patina --preview --jargon remove https://example.com/ # de-jargonized in-place preview
-patina --jargon explain --tone casual draft.md # gloss terms, casual register
+patina --jargon explain --register casual draft.md # gloss terms, casual register
```
- `--jargon keep` (default) — technical terms untouched.
@@ -127,17 +165,19 @@ patina --jargon explain --tone casual draft.md # gloss terms, casual reg
### Variant comparison in the preview
-With `--preview`, `--jargon` **and `--tone`** accept comma-separated lists; every combination becomes a **variant** — one rewrite call each, capped at 4 — and the preview bar gains a second toggle group to switch between them in place:
+With `--preview`, `--jargon` and `--register` accept comma-separated lists;
+every combination becomes a variant — one rewrite call each, capped at 4 — and
+the preview bar gains a second toggle group:
```bash
-patina --preview --jargon keep,remove # cleanup / de-jargoned side by side
-patina --preview --jargon remove --tone casual,professional # same policy, two voices
-patina --preview --tone casual,professional # register comparison
+patina --preview --jargon keep,remove
+patina --preview --jargon remove --register casual,professional
+patina --preview --register casual,professional
```
-- The bar groups variants two-level: one primary button per jargon policy (cleanup/explain/remove) and, when a policy carries multiple options (tone), a secondary chip row that appears only while that policy is selected — click **remove**, then pick **casual** or **professional**. Each policy remembers its own option selection. The switch is CSS-only (chained radio groups), so the snapshot stays scriptless and the page CSP keeps `script-src 'none'`.
+- The bar groups variants by jargon policy, with a secondary Register chip row when needed.
- The score chip shows each variant's deterministic score (`score 23 → cleanup 5 · remove 8`).
-- A comma-listed `--tone` joins the cross product: each variant resolves its own register (genre profile is fixed by `--profile`), exactly as a single run with that `--tone` would. Labels carry the tone when it varies (`remove·casual`).
+- A comma-listed `--register` joins the cross product. Document Type and Persona stay fixed.
- A block counts as changed when **any** variant changes it; a variant that left a block alone shows the original text under that button.
- stdout carries the first variant's prose (pipe-safe); the explanation call is skipped in compare mode to keep the call budget at one per variant.
- Compare mode needs a page snapshot (URL or `.html`) and is incompatible with `--ocr`; comma lists without `--preview` are an input error.
@@ -146,7 +186,7 @@ patina --preview --tone casual,professional # register compariso
The view toggle has four states: **rewritten** (default), **original**, **both**, and **diff**. The diff view renders each changed block as one merged stream — common words plain, removed words struck red, added words highlighted green — so the exact edit is visible instead of a whole-sentence strikethrough. It is computed deterministically when the page is built (LCS over whitespace tokens, matrix-capped with a whole-text del/ins fallback for huge blocks) and works per variant in compare mode.
-In every depth, facts, numbers, names, and causal claims must never be invented, dropped, or reversed — the directive relaxes style and structure, not truth.
+Facts, numbers, names, and causal claims must never be invented, dropped, or reversed. Transform options change terminology or delivery, not truth.
## Stderr logs
@@ -176,7 +216,7 @@ URL contract:
Document context:
- Rewrites run under a **document brief**: the prompt instructs the model to first identify what the document is, who is speaking to whom, the dominant register, and the recurring domain terms — and to keep that frame for every edit. All rewritten sentences are unified to the document's dominant register (register mixing is itself an AI tell).
- For Korean text the dominant register is **measured deterministically** (sentence-ending distribution: 합쇼체/해요체/-다체) and injected into the prompt as ground truth; the "patina notes" panel shows the measurement in a *document context* card.
-- `--tone ` works with `--preview` and overrides the target register; the register-unification rule still applies. (academic/marketing/narrative/instructional are genres — use `--profile`.)
+- `--register ` works with `--preview` and overrides delivery; omission preserves the source register. Genre values such as `academic`, `marketing`, and `narrative` belong to `--document-type`.
File contract (local `.html`):
- A local `.html`/`.htm` file goes through the same snapshot pipeline as a fetched URL: prose blocks are extracted, rewritten, and swapped back in place. Markdown/text drafts are not accepted as preview input.
@@ -299,4 +339,4 @@ What it does, in order: detect and normalize the file's `target-language` (`ko-K
- **`--max-segments `** overrides the default cap of 50 unique segments per file (the cap is enforced after dedup, before any LLM call; over the cap fails closed in execution mode and is flagged in `--dry-run`).
- **Cross-file dedup (`--batch`).** Deduplication also spans the whole batch: a segment repeated across files (same target-language + text) is humanized once and the verified result is reused for every other file — no extra LLM calls. Reuse is scoped by target-language (identical text in a `ko` file and a `ja` file is humanized separately), errors are never cached (the next file retries), and `--dry-run` reports the cross-file reuse count.
-`--xliff` accepts backend/model/provider/base-url/auth/timeout/concurrency/retry/failure-budget flags and `--suffix`/`--outdir`/`--in-place`/`--batch`/`--format`. It rejects rewrite-shaping and other-mode flags (`--audit`/`--score`/`--diff`/`--preview`/`--ocr`/`--serve`/`--exit-on`/`--persona`/`--jargon`/`--tone`/`--profile`/`--rewrite-headings`/`--verify`) with a clear input error. `--dry-run` and `--max-segments` are valid only with `--xliff`.
+`--xliff` accepts backend/model/provider/base-url/auth/timeout/concurrency/retry/failure-budget flags and `--suffix`/`--outdir`/`--in-place`/`--batch`/`--format`. It rejects rewrite-shaping and other-mode flags (`--audit`/`--score`/`--diff`/`--preview`/`--ocr`/`--serve`/`--exit-on`/`--persona`/`--jargon`/`--register`/`--document-type`/`--rewrite-headings`/`--verify`) with a clear input error. `--dry-run` and `--max-segments` are valid only with `--xliff`.
diff --git a/docs/COOKBOOK.md b/docs/COOKBOOK.md
index f34adf2..bf1e86b 100644
--- a/docs/COOKBOOK.md
+++ b/docs/COOKBOOK.md
@@ -2,7 +2,7 @@
Practical recipes for plugging patina into existing writing and CI workflows. Each recipe is self-contained — copy, adapt, run.
-For the full flag list see `patina --help` and [`CLI.md`](CLI.md). For persona, tone, and profile background see [`README.md`](../README.md#modes).
+For the full flag list see `patina --help` and [`CLI.md`](CLI.md). For Document Type, Persona, and Register see [`README.md`](../README.md#three-independent-axes).
---
@@ -12,15 +12,15 @@ You have a Hugo site with many drafts under `content/posts/`. You want a quick A
```bash
# from your Hugo project root
-patina --lang en --score --batch content/posts/*.md
+patina --lang en --score --offline --batch content/posts/*.md
```
-`--batch` treats every positional arg as an input file, so any glob your shell expands works. `--score` per file prints `overall` plus the category breakdown.
+`--batch` treats every positional arg as an input file, so any glob your shell expands works. `--offline` makes this a reproducible local check with no backend. Omit it when you want the LLM-judged categories reconciled with the same deterministic signals.
For a stricter sweep that flags anything above 30/100, fail the run instead of just printing:
```bash
-patina --lang en --score --exit-on 30 --batch content/posts/*.md
+patina --lang en --score --offline --exit-on 30 --batch content/posts/*.md
```
When any file's `overall` exceeds the gate, patina exits with code `3` ([`CLI.md`](CLI.md) §Exit codes), which is perfect for a pre-publish check.
@@ -53,13 +53,10 @@ jobs:
run: |
changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- '*.md')
[ -z "$changed" ] && echo "no markdown changes" && exit 0
- patina --lang en --score --exit-on 30 --batch $changed
- env:
- # pick one backend that has a token in repo secrets
- GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
+ patina --lang en --score --offline --exit-on 30 --batch $changed
```
-Drop `--exit-on` while you calibrate the threshold for your project. Swap `GEMINI_API_KEY` for whichever backend you have (`claude` / `codex` / `gemini`) — see [`AUTHENTICATION.md`](AUTHENTICATION.md) for the full list.
+Drop `--exit-on` while you calibrate the threshold for your project. Remove `--offline` and configure a backend only if the workflow needs LLM-judged categories; see [`AUTHENTICATION.md`](AUTHENTICATION.md).
---
@@ -98,42 +95,51 @@ skip-patterns:
---
-## 5. Create a custom profile (copy `blog.md`, edit pattern policy)
+## 5. Create a custom Document Type policy
-Use a custom profile to apply deterministic pattern policy for a house style. Persona is the reusable voice-composition control; tone is the Korean/English `casual` / `professional` / `auto` register override and wins over a persona's register. A profile is not the place to compose a new persona.
-
-When the built-in profiles do not match your local pattern policy, fork the closest one:
+Document Type owns genre, purpose, structural conventions, and pattern policy.
+Persona and Register remain independent. In a source checkout or managed patina
+installation, start from the nearest built-in policy:
```bash
-cp profiles/blog.md profiles/my-newsletter.md
+cp document-types/blog.md document-types/my-newsletter.md
```
-Edit the frontmatter — at minimum change `profile:`, then tune `pattern-overrides` for the policy you need:
+Change the frontmatter id so it matches the filename, define the document
+conventions, then tune `pattern-overrides`:
```yaml
---
-profile: my-newsletter # must match the filename without .md
-name: Internal newsletter profile
+document-type: my-newsletter
+name: Internal newsletter
version: 1.0.0
scope: weekly engineering newsletter
+purpose: summarize shipped engineering work and the next actions
+audience:
+ - engineers and internal stakeholders
+structure:
+ - lead with shipped changes, then impact, risks, and explicit next actions
+style:
+ - use concrete component names and evidence
+avoid:
+ - inventing delivery dates, owners, metrics, or commitments
pattern-overrides:
en:
- 14: suppress # bold is allowed for scannable sections
- 7: amplify # AI-vocab cleanup stays strict
+ 14: suppress
+ 7: amplify
---
```
Then opt in per run:
```bash
-patina --lang en --profile my-newsletter post.md
+patina --lang en --document-type my-newsletter post.md
```
-Pattern-policy values are `amplify` / `allow` / `reduce` / `suppress`; pattern IDs and their meanings are in [`PATTERNS.md`](PATTERNS.md).
-
-> **What actually runs:** a `pattern-overrides` entry set to **`suppress`** is applied deterministically — patina drops that pattern from the rewrite / audit / score prompt for the profile's language, so the model never flags it (e.g. `legal` suppresses Korean passive-voice #27). `reduce` / `amplify` are advisory for now: they document intent, but the engine does not yet adjust their weight.
->
-> **Current vs. target:** profiles currently supply deterministic pattern policy, but some skill and core-prompt paths still carry legacy voice guidance. “Profile is pattern-policy only everywhere” is therefore a target, not a universal current invariant. This release changes no prompts or runtime behavior.
+`suppress` is enforced deterministically by removing that pattern from the
+prompt. `reduce` and `amplify` currently document policy intent but do not change
+a runtime weight. For a project-local exclusion that does not require a custom
+Document Type file, use `skip-patterns` in `.patina.yaml`.
---
@@ -146,7 +152,7 @@ Block commits that introduce too-AI-sounding markdown. Drop this into `.git/hook
set -euo pipefail
changed=$(git diff --cached --name-only --diff-filter=ACM -- '*.md')
[ -z "$changed" ] && exit 0
-patina --lang en --score --exit-on 30 --batch $changed
+patina --lang en --score --offline --exit-on 30 --batch $changed
```
`--exit-on` returns exit code `3` when any file's `overall` exceeds the threshold, which the shell treats as failure and aborts the commit. To bypass once (e.g. you intentionally want hype copy), commit with `--no-verify`.
@@ -169,11 +175,11 @@ backend requires one.
Three pitfalls, all observed in practice:
-1. **Context size.** patina's rewrite prompt (pattern digests + profile + advisory
- guidance) runs 15–20k tokens, and Ollama's default context is 4096. Newer Ollama
- fails loudly (`exceed_context_size_error`); older versions **silently truncate
- the prompt**, which quietly degrades rewrite quality. Start the server with
- `OLLAMA_CONTEXT_LENGTH=24576` (or higher) before testing anything.
+1. **Context size.** patina's rewrite prompt (pattern digests + Document Type +
+ voice guidance) can exceed a small local model's default context. Newer
+ Ollama versions fail loudly (`exceed_context_size_error`); older versions may
+ silently truncate the prompt, which degrades rewrite quality. Start the
+ server with `OLLAMA_CONTEXT_LENGTH=24576` (or higher) before testing.
2. **`--verify` is not optional for small local models.** A 12B model will happily
round `38%` to "nearly 40%" and drop the survey year while producing an
otherwise fluent rewrite. `--verify` runs the MPS/fidelity floors plus the
@@ -189,14 +195,14 @@ Set `--timeout-ms` generously: a 12B model on an 8 GB GPU takes minutes per rewr
at full prompt length, not seconds. When the per-attempt budget exceeds 300s, the
HTTP backend automatically switches to SSE streaming so Node's undici
`headersTimeout` cannot kill a slow local generation mid-flight (#576). Judge
-candidates with the deterministic score (`patina --score` on the rewrite output),
-not vibes — and prefer comparing against a cloud backend baseline on the same input.
+candidates with the deterministic score (`patina --score --offline` on the
+rewrite output), not vibes — and compare against a cloud backend baseline on the same input.
---
## Where to go next
-- Tone reference: [`README.md`](../README.md#tones)
-- Free-tier setup (no API key): [`AUTHENTICATION.md`](AUTHENTICATION.md)
+- Axis reference: [`README.md`](../README.md#three-independent-axes)
+- Backend setup: [`AUTHENTICATION.md`](AUTHENTICATION.md)
- MPS and other terms: [`GLOSSARY.md`](GLOSSARY.md)
- Adding patterns or false-positive triage: [`CONTRIBUTING.md`](../CONTRIBUTING.md)
diff --git a/docs/DEMO.md b/docs/DEMO.md
index 9c783da..082644f 100644
--- a/docs/DEMO.md
+++ b/docs/DEMO.md
@@ -14,7 +14,7 @@ This transcript uses the checked-in English marketing fixture so the example is
$ cat examples/short/marketing-launch-en.md
The newly released Notion template pack is an innovative solution designed to transform productivity for modern teams. It offers 30 templates optimized for diverse workflows, with a user-friendly design that enables anyone to leverage them effortlessly. This product introduces a new paradigm for maximizing work efficiency.
-$ patina --lang en --tone marketing examples/short/marketing-launch-en.md
+$ patina --lang en --document-type marketing examples/short/marketing-launch-en.md
If Notion still starts as a blank page for your team, open this pack first. It includes 30 templates for common workflows. Duplicate one, adjust the fields you need, and use it for a team project or your own planning without starting from scratch.
```
@@ -30,7 +30,7 @@ This is the Korean fixture used by `README_KR.md`.
$ cat examples/short/marketing-launch.md
새롭게 출시된 노션 템플릿 팩은 생산성 향상을 위한 혁신적인 솔루션입니다. 다양한 워크플로우에 최적화된 30개의 템플릿을 제공하며, 사용자 친화적인 디자인으로 누구나 손쉽게 활용 가능합니다. 본 제품은 업무 효율성을 극대화하는 새로운 패러다임을 제시합니다.
-$ patina --lang ko --tone marketing examples/short/marketing-launch.md
+$ patina --lang ko --document-type marketing examples/short/marketing-launch.md
노션을 자주 쓰지만 매번 빈 페이지에서 막힌다면 이 팩부터 열어 보세요. 업무별 템플릿 30개를 담았습니다. 복잡한 설정 없이 복제해서 바로 고치고, 팀 프로젝트든 개인 정리든 필요한 형태로 손보면 됩니다.
```
diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md
index 0d1e131..9964487 100644
--- a/docs/EXAMPLES.md
+++ b/docs/EXAMPLES.md
@@ -72,7 +72,7 @@ patina is easiest to understand with examples: the claims stay, the AI packaging
> 처음 코드 리뷰를 받던 날이 아직도 가끔 떠오른다. 선배가 남긴 피드백은 한 줄씩 읽을 때마다 이상하게 크게 느껴졌다. 모니터 앞에서 댓글을 하나씩 열어보던 장면이 선명하다. 손은 마우스 위에 있었고, 머릿속은 자꾸 하얘졌다. 지금 생각하면 그날은 대단한 사건이라기보다, 내가 처음으로 내 코드를 남의 눈으로 본 날에 가까웠다.
-**Patterns removed**: `마치 거대한 산처럼` (ko-content abstract metaphor), `의미 있는 전환점이었다고 표현할 수 있을 것이다` (ko-structure #25 hedge stack + ko-content inflated significance). Replaced with concrete physical detail (`손은 마우스 위에`, `머릿속은 자꾸 하얘졌다`) — the narrative profile's `concrete-emotions: amplify` setting.
+**Patterns removed**: `마치 거대한 산처럼` (ko-content abstract metaphor), `의미 있는 전환점이었다고 표현할 수 있을 것이다` (ko-structure #25 hedge stack + ko-content inflated significance). Replaced with concrete physical detail (`손은 마우스 위에`, `머릿속은 자꾸 하얘졌다`) — the narrative Document Type's policy.
## Korean: SNS marketing — viral-hook signals
@@ -116,8 +116,8 @@ This page shows the canonical short examples. The repo also ships longer fixture
- **`examples/short/`** — four short Korean fixtures (marketing, tutorial, essay, email) with paired `*-rewritten.md` files.
- **`examples/genres/`** — three longer Korean genres (technical, academic, narrative) with paired rewrites.
-- **`examples/tones/`** — the same input rewritten in six tones (`casual`, `professional`, `academic`, `narrative`, `marketing`, `instructional`) plus `auto`. See `examples/tones/RESULTS.md` for the side-by-side.
-- **`examples/viral-hook/`** — case studies (`case-01` through `case-09`) covering the iterative improvement workflow: viral-hook detection, codex/claude/gemini comparisons, voice profiles, multi-genre validation.
+- **`examples/rewrite-axes/`** — v7 axis fixtures. `casual`/`professional` demonstrate Register; `academic`/`narrative`/`marketing`/`instructional` demonstrate Document Type. Every rewrite preserves claim strength and avoids invented examples, people, scenes, or results.
+- **`examples/viral-hook/`** — case studies (`case-01` through `case-09`) covering the iterative improvement workflow: viral-hook detection, codex/claude/gemini comparisons, Persona voice, and multi-Document-Type validation.
- **`examples/sample-rewritten-*.md`** — the same long-form Korean SNS marketing post rewritten by Codex / Claude / Gemini-3, used in `case-03` for cross-model comparison.
## What patina is checking
diff --git a/docs/EXAMPLES_KR.md b/docs/EXAMPLES_KR.md
index f238e8b..9eac08b 100644
--- a/docs/EXAMPLES_KR.md
+++ b/docs/EXAMPLES_KR.md
@@ -72,7 +72,7 @@ patina는 예시로 보는 편이 가장 쉽습니다. 주장은 남고, AI 포
> 처음 코드 리뷰를 받던 날이 아직도 가끔 떠오른다. 선배가 남긴 피드백은 한 줄씩 읽을 때마다 이상하게 크게 느껴졌다. 모니터 앞에서 댓글을 하나씩 열어보던 장면이 선명하다. 손은 마우스 위에 있었고, 머릿속은 자꾸 하얘졌다. 지금 생각하면 그날은 대단한 사건이라기보다, 내가 처음으로 내 코드를 남의 눈으로 본 날에 가까웠다.
-**제거한 패턴**: `마치 거대한 산처럼` (ko-content abstract metaphor), `의미 있는 전환점이었다고 표현할 수 있을 것이다` (ko-structure #25 hedge stack + ko-content inflated significance). 구체적인 신체 디테일(`손은 마우스 위에`, `머릿속은 자꾸 하얘졌다`)로 바꿨습니다. narrative profile의 `concrete-emotions: amplify` 설정입니다.
+**제거한 패턴**: `마치 거대한 산처럼` (ko-content abstract metaphor), `의미 있는 전환점이었다고 표현할 수 있을 것이다` (ko-structure #25 hedge stack + ko-content inflated significance). 구체적인 신체 디테일(`손은 마우스 위에`, `머릿속은 자꾸 하얘졌다`)로 바꿨습니다. narrative Document Type의 정책입니다.
## Korean: SNS marketing — viral-hook signals
@@ -116,8 +116,8 @@ patina는 예시로 보는 편이 가장 쉽습니다. 주장은 남고, AI 포
- **`examples/short/`** — 네 개의 짧은 Korean fixture(marketing, tutorial, essay, email)와 짝을 이루는 `*-rewritten.md` 파일.
- **`examples/genres/`** — 세 개의 긴 Korean genre(technical, academic, narrative)와 짝을 이루는 rewrite.
-- **`examples/tones/`** — 같은 입력을 여섯 tone(`casual`, `professional`, `academic`, `narrative`, `marketing`, `instructional`)과 `auto`로 rewrite한 결과. 나란히 보려면 `examples/tones/RESULTS.md`를 참고하세요.
-- **`examples/viral-hook/`** — iterative improvement workflow를 다루는 case study(`case-01`부터 `case-09`): viral-hook detection, codex/claude/gemini comparison, voice profile, multi-genre validation.
+- **`examples/rewrite-axes/`** — v7 축 fixture. `casual`/`professional`은 Register를, `academic`/`narrative`/`marketing`/`instructional`은 Document Type을 보여 줍니다. 모든 rewrite는 주장의 강도를 보존하고 원문에 없는 사례·인물·장면·성과를 만들지 않습니다.
+- **`examples/viral-hook/`** — iterative improvement workflow를 다루는 case study(`case-01`부터 `case-09`): viral-hook detection, codex/claude/gemini comparison, Persona voice, multi-Document-Type validation.
- **`examples/sample-rewritten-*.md`** — 같은 장문의 Korean SNS marketing post를 Codex / Claude / Gemini-3로 rewrite한 결과. `case-03`에서 cross-model comparison에 사용합니다.
## patina가 확인하는 것
diff --git a/docs/FALSE-POSITIVES.md b/docs/FALSE-POSITIVES.md
index 2e0419a..40abd20 100644
--- a/docs/FALSE-POSITIVES.md
+++ b/docs/FALSE-POSITIVES.md
@@ -62,6 +62,6 @@ Open a false-positive issue with:
2. a safe sample you are allowed to share;
3. the Patina score or audit output;
4. why the passage should remain acceptable in that register;
-5. whether the fix should be an exclusion, lower severity, profile override, or benchmark fixture.
+5. whether the fix should be an exclusion, lower severity, Document Type `pattern-overrides` change, or benchmark fixture.
Maintainer side: accepted reports follow the intake path in `docs/research/humanization-data-backlog.md` ("False-Positive Feedback Intake Path") from issue to intake row to benchmark fixture.
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 649aa3a..51bb051 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -18,9 +18,11 @@ The goal is not to make a text deceptive. The goal is to remove generic model vo
## How does patina preserve meaning?
-patina extracts semantic anchors before rewriting: claims, polarity, causation, numbers, negation, and other high-risk details. After each rewrite phase, it checks whether those anchors are still present and whether their polarity stayed the same.
-
-If a rewrite weakens, deletes, or reverses an anchor, patina retries the section or rolls it back.
+Meaning preservation is global; Document Type, Persona, and Register cannot
+lower its thresholds. Every CLI rewrite runs a deterministic dropped-number
+guard. `--verify` adds model-scored MPS/fidelity floors and one conservative
+retry; the agent skill's `--strict` flow adds its documented retry/rollback
+gates.
## What is MPS?
@@ -32,7 +34,10 @@ A high MPS does not mean the prose is perfect. It means the rewrite did not obvi
The score is a rough editing signal from 0 to 100. Lower is less AI-sounding.
-It is not a truth machine. The scoring formula is deterministic, but severity assignment can vary by roughly 8-10 points between model runs. Treat the range and the highlighted patterns as more important than the exact number.
+It is not a truth machine. Default `--score` combines an LLM judgment with
+deterministic signals and can vary between model runs. `--score --offline`
+reports only reproducible local signals. Treat the range and highlighted
+patterns as more important than an exact number.
## How accurate is it?
@@ -46,15 +51,20 @@ See [ETHICS.md](ETHICS.md) for the intended-use position statement.
## Does it work without an API key?
-Yes, if you already have the Codex CLI installed and logged in. The installer can wire patina into Codex CLI as a backend, so no separate API key is required for that path.
-
-Other providers can be configured through the documented backend and provider settings.
+Yes. `--score --offline` and the `patina-score` precommit gate need no backend.
+LLM-backed modes can use a logged-in local Codex, Claude, Gemini, or Kimi CLI
+instead of an API key. See [Authentication](AUTHENTICATION.md).
## Does patina send my text anywhere?
-The web playground runs entirely in your browser. It makes no network calls and loads no analytics or trackers, so text you paste into it never leaves the page.
+CLI deterministic analysis stays local. LLM-backed CLI modes send text only to
+the backend you select, which may be a local CLI, local/self-hosted endpoint, or
+remote API.
-The CLI runs its deterministic analysis (the audit and the score) locally. The only step that sends text off your machine is the rewrite, and it goes to the backend you choose: a CLI you are already logged into, such as Codex, Claude, Gemini, or Kimi, under your own account, or an OpenAI-compatible API using your own key and base URL (the default is the official OpenAI endpoint, but you can point it at a local or self-hosted model). patina runs no server of its own, ships no telemetry, and never sends your text to a patina-owned endpoint.
+The hosted playground sends rewrite and scoring requests to patina's server.
+Free requests use the server provider; BYOK credentials are forwarded for that
+request and are not stored or logged; Pro uses a Lemon Squeezy license key
+validated server-side. The browser never calls an LLM provider directly.
## Does it only work in Claude Code?
@@ -64,11 +74,13 @@ No. patina runs as a skill for Claude Code, Codex CLI, Cursor, and OpenCode, and
Korean, English, Chinese, and Japanese are supported. Pattern packs are auto-discovered by language prefix, so new languages can be added by contributing new pattern files.
-## Do persona, profile, and tone overlap?
-
-They serve different jobs. A persona owns reusable voice composition. In Korean and English, `--tone casual|professional|auto` is the register override and takes precedence over a persona's register. A profile supplies deterministic pattern policy; use a custom pattern pack for separate local rules.
+## Do Document Type, Persona, and Register overlap?
-That separation is the current direction, not a claim that every path has reached the target: some skill and core-prompt paths still carry legacy voice guidance from profiles. This release does not change prompts or runtime behavior, merge the axes, rename `--tone`, or expose new hosted controls. Completing profile-policy-only handling everywhere, verifying retry-context preservation, and considering a user-facing “Register” name are later work.
+No. Document Type controls genre, purpose, structural conventions, and pattern
+policy. Persona v2 is an optional reusable voice fingerprint; omission preserves
+the source voice. Register controls only `casual` or `professional` delivery;
+omission preserves the source register. Meaning floors and verification are
+global and independent of all three axes.
## What should contributors start with?
diff --git a/docs/FAQ_KR.md b/docs/FAQ_KR.md
index 2736910..ea8925f 100644
--- a/docs/FAQ_KR.md
+++ b/docs/FAQ_KR.md
@@ -16,9 +16,10 @@ AI detector는 잡음이 많습니다. patina는 어떤 score도 텍스트가
## patina는 의미를 어떻게 보존하나요?
-patina는 rewrite 전에 semantic anchor를 뽑습니다. 주장, 극성, 인과, 숫자, 부정, 그 밖의 위험도가 높은 세부 정보를 추적합니다. 각 rewrite 단계 뒤에는 그 anchor가 여전히 있는지, 극성이 그대로인지 확인합니다.
-
-rewrite가 anchor를 약화하거나 삭제하거나 뒤집으면, patina는 해당 구간을 다시 시도하거나 되돌립니다.
+의미 보존은 전역 규칙입니다. Document Type, Persona, Register는 안전 기준을
+낮출 수 없습니다. CLI rewrite에는 항상 숫자 누락 검사가 적용됩니다.
+`--verify`는 MPS/fidelity 기준과 한 번의 보수적인 retry를 추가하고, agent
+skill의 `--strict`는 문서화된 retry/rollback gate를 적용합니다.
## MPS가 무엇인가요?
@@ -30,7 +31,9 @@ MPS가 높다고 해서 문장이 완벽하다는 뜻은 아닙니다. patina가
score는 0부터 100까지의 대략적인 편집 신호입니다. 낮을수록 AI처럼 덜 들립니다.
-이 값은 진실 판정기가 아닙니다. scoring formula는 deterministic이지만 severity assignment는 모델 실행 사이에 대략 8-10점 정도 달라질 수 있습니다. 정확한 숫자보다 범위와 하이라이트된 패턴을 더 중요하게 보세요.
+진실 판정기는 아닙니다. 기본 `--score`는 LLM 판단과 deterministic signal을
+결합하므로 모델 실행마다 달라질 수 있습니다. `--score --offline`은 로컬에서
+재현 가능한 signal만 보고합니다. 정확한 숫자보다 범위와 탐지된 패턴을 보세요.
## 정확도는 어느 정도인가요?
@@ -44,9 +47,20 @@ score는 0부터 100까지의 대략적인 편집 신호입니다. 낮을수록
## API key 없이도 동작하나요?
-네. 이미 Codex CLI를 설치하고 로그인했다면 가능합니다. installer는 patina를 Codex CLI backend에 연결할 수 있으므로 이 경로에서는 별도 API key가 필요하지 않습니다.
+네. `--score --offline`과 `patina-score` precommit gate에는 backend가
+필요하지 않습니다. LLM 기반 모드는 API key 대신 로그인된 Codex, Claude,
+Gemini, Kimi CLI를 사용할 수 있습니다. [Authentication](AUTHENTICATION.md)을
+참고하세요.
+
+## 입력한 텍스트가 외부로 전송되나요?
-다른 provider는 문서화된 backend/provider 설정으로 구성할 수 있습니다.
+CLI의 deterministic analysis는 로컬에서 실행됩니다. LLM 기반 CLI 모드는
+사용자가 선택한 backend로만 텍스트를 보냅니다.
+
+hosted playground의 rewrite와 score 요청은 patina 서버로 전송됩니다. free는
+서버 provider를 사용하고, BYOK key는 요청 단위로 전달되며 저장·로그되지
+않습니다. Pro license는 서버에서 Lemon Squeezy로 검증합니다. 브라우저가
+LLM provider를 직접 호출하지는 않습니다.
## Claude Code에서만 동작하나요?
@@ -56,11 +70,13 @@ score는 0부터 100까지의 대략적인 편집 신호입니다. 낮을수록
한국어, 영어, 중국어, 일본어를 지원합니다. 패턴 팩은 언어 접두사로 자동 탐색되므로 새 언어는 새 패턴 파일을 기여해 추가할 수 있습니다.
-## persona, profile, tone은 서로 겹치나요?
-
-역할이 다릅니다. persona는 재사용 가능한 voice composition을 맡습니다. 한국어와 영어에서 `--tone casual|professional|auto`는 register override이며 persona의 register보다 우선합니다. profile은 deterministic pattern policy를 제공하며, 별도의 로컬 규칙에는 custom pattern pack을 사용하세요.
+## Document Type, Persona, Register는 서로 겹치나요?
-이 구분은 현재 방향이지 모든 경로가 이미 목표에 도달했다는 뜻은 아닙니다. 일부 skill 및 core-prompt 경로에는 profile의 legacy voice guidance가 아직 남아 있습니다. 이번 릴리스는 prompt나 runtime behavior를 바꾸지 않으며, 축을 합치거나 `--tone`을 이름 변경하거나 새 hosted control을 노출하지 않습니다. 모든 경로에서 profile을 pattern policy로만 완성하는 일, retry context preservation 검증, 사용자 대상 “Register” 명칭 검토는 이후 작업입니다.
+아닙니다. Document Type은 장르·용도·구조 관습·pattern policy를 정합니다.
+Persona v2는 선택 가능한 재사용 voice fingerprint이며, 생략하면 원문
+voice를 보존합니다. Register는 `casual`/`professional` 전달 방식만 정하며,
+생략하면 원문 register를 보존합니다. 의미 보존 기준과 verification은 세
+축과 독립된 전역 규칙입니다.
## 기여자는 무엇부터 시작하면 좋나요?
diff --git a/docs/FLAG-PARITY.md b/docs/FLAG-PARITY.md
index 6a26d82..5352c22 100644
--- a/docs/FLAG-PARITY.md
+++ b/docs/FLAG-PARITY.md
@@ -20,8 +20,11 @@ Basis: local checkout plus `node bin/patina.js --help` and `SKILL.md` reviewed o
| `--outdir ` | ✓ | ✓ | Batch-only output directory. |
| `--no-interactive` | ✓ | — | CLI input safety for non-TTY automation. |
| `--lang ` | ✓ | ✓ | `ko`, `en`, `zh`, `ja`. |
-| `--profile ` | ✓ | ✓ | Profile override. |
-| `--tone ` | ✓ | ✓ | Shared tone surface. |
+| `--document-type ` | ✓ | ✓ | Genre, purpose, structural conventions, and pattern policy. |
+| `--persona ` | ✓ | ✓ | Optional reusable voice; rewrite/preview only in CLI. |
+| `--register ` | ✓ | ✓ | Delivery override; omission preserves source register. |
+| `--jargon ` | ✓ | — | CLI rewrite/preview terminology policy. |
+| `--preview` | ✓ | — | CLI URL/local-HTML in-place preview. |
| `--model ` | ✓ | — | CLI single-backend model selection. |
| `--api-key-file ` | ✓ | — | CLI auth. |
| `--base-url ` | ✓ | — | CLI provider/backend config. |
@@ -35,6 +38,7 @@ Basis: local checkout plus `node bin/patina.js --help` and `SKILL.md` reviewed o
| `-v`, `--version` | ✓ | — | CLI version. |
| `patina doctor` | ✓ | — | CLI environment diagnostic. |
| `patina auth status/login` | ✓ | — | CLI authentication guidance. |
+| `patina persona new/list/show/edit/rm` | ✓ | — | CLI custom Persona lifecycle. |
## Audit notes
diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md
index b0b472f..93a14a1 100644
--- a/docs/GLOSSARY.md
+++ b/docs/GLOSSARY.md
@@ -28,11 +28,20 @@ The mode that shows changes pattern by pattern, making the edit auditable
instead of a black-box paraphrase. See the [mode table](../README.md#modes)
and [examples checklist](EXAMPLES.md#what-patina-is-checking).
+## Document Type
+
+The document-policy axis. It selects purpose, audience, structural conventions,
+domain vocabulary/precision, avoidance rules, and language-scoped pattern
+bounds without changing Persona voice, Register, or meaning thresholds. CLI:
+`--document-type`; config: `document-type`. The historical ids `formal` and
+`casual-conversation` refer to structured-document and chat/message conventions;
+they do not select professional or casual Register.
+
## Fidelity
-A meaning-accuracy score comparing original text with the output. Higher is
-better: it checks whether claims, facts, tone, and length stayed faithful. See
-[Fidelity Scoring](../core/scoring.md#9-fidelity-scoring--overview).
+A meaning-accuracy score comparing original text with a rewrite. Higher is
+better: it checks whether claims, facts, audience/register, and length stayed
+faithful. See [Fidelity Scoring](../core/scoring.md#9-fidelity-scoring--overview).
## Meaning preservation
@@ -49,9 +58,9 @@ language limitations. See [Stylometry](../core/stylometry.md#5-ttr-via-mattr).
## Mode
-A CLI output path such as rewrite, verify, audit, score, or diff. Modes
-control whether patina edits text, verifies meaning preservation, reports
-findings, or scores text. See the [mode table](../README.md#modes).
+A CLI output path such as rewrite, audit, score, or diff. Modes control whether
+patina edits text or reports findings. `--verify` is a rewrite modifier, not a
+separate mode. See the [mode table](../README.md#modes).
## MPS
@@ -73,11 +82,19 @@ A language and category file such as `patterns/en-style.md` or
`patterns/ko-content.md`. Packs group related patterns and provide counts used
by scoring. See [PATTERNS.md](PATTERNS.md#language-specific-patterns).
-## Profile
+## Persona
+
+An optional, reusable voice fingerprint. Persona v2 may shape vocabulary,
+rhythm, metaphors, and explanation habits; it cannot select Document Type,
+Register, pattern policy, or meaning thresholds. Omitting `--persona` preserves
+the source voice.
-A voice preset that can amplify, reduce, or suppress specific patterns for a
-use case such as blog, academic, technical, or legal writing. See
-[profile override adjustments](../core/scoring.md#5-profile-override-adjustments).
+## Register
+
+The `casual` or `professional` delivery axis: wording and sentence-ending
+markers only. Omitting `--register` preserves the source register. Genre values
+such as `academic` and `marketing` are Document Types, while idiolect and rhythm
+belong to Persona.
## Rewrite mode
@@ -87,8 +104,9 @@ meaning survived the edit. See the [Quick Start](../README.md#quick-start) and
## Score mode
-The mode that returns a 0-100 AI-likeness score with category breakdowns and,
-when original text is available, fidelity and MPS signals. See
+The mode that returns a 0-100 AI-likeness score. Default scoring combines
+LLM-judged categories with deterministic signals; `--offline` reports the
+deterministic subset without a backend. See
[Score Interpretation](../core/scoring.md#7-score-interpretation).
## Semantic anchor
@@ -103,11 +121,6 @@ A paragraph or sentence group marked for closer inspection by stylometry,
usually because burstiness, MATTR, or lexicon signals fired. See
[the stylometry delivery format](../core/stylometry.md#9-llm-delivery-format).
-## Tone
-
-A named voice axis, such as casual, professional, academic, narrative,
-marketing, instructional, or auto. Tone is applied on top of pattern rewriting.
-See [Tones](../README.md#tones).
## Viral hook
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index d243b8f..31a4f7a 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -186,31 +186,32 @@ Acceptance criteria:
- Integration docs are tested manually before public launch posts.
- Each integration has one minimal example and one realistic example.
-### Phase 4 — custom persona authoring
+### Phase 4 — custom Persona authoring (shipped in 7.0.0)
-**Voice-axis baseline (release 7.0.0 documentation):** persona owns reusable voice composition. In Korean and English, `--tone casual|professional|auto` is the register override and wins over a persona's register. A profile currently supplies deterministic pattern policy.
+The shipped axis contract is explicit:
-This is a current-vs-target distinction: some skill and core-prompt paths still carry legacy voice guidance from profiles, so “profile is pattern-policy only everywhere” is not yet a universal invariant. This release changes no prompts or runtime behavior, does not merge the axes or rename `--tone`, and exposes no new hosted controls.
+- Document Type owns genre, purpose, structural conventions, and pattern policy.
+- Persona v2 is optional and owns only reusable voice.
+- Register owns only `casual` or `professional` delivery.
+- Meaning preservation and verification are global; no axis can weaken them.
-Later work, not shipped behavior:
-- Complete profile-policy-only handling across the remaining paths.
-- Verify retry-context preservation when persona, tone, and profile are combined.
-- Consider whether user-facing copy should call the register control “Register” while retaining `--tone`.
+`patina persona new|list|show|edit|rm` covers the custom Persona lifecycle in
+ko/en/zh/ja. Omitting `--persona` preserves the source voice. The v7 CLI rejects
+the retired `--profile`, `--tone`, and `--formality` inputs with migration
+errors rather than aliases.
-Goal: let users define and reuse a persona rather than supplying raw style samples.
+Later work, not shipped behavior:
-- Build on the existing persona harness (`src/personas/`, `personas/ko/`) and the separate `--persona` / `--tone` / `--profile` axes rather than a sample-injection path.
-- Provide an authoring entry point so a user can create and reuse a named custom persona for voice composition and genre; tone remains the run-level register override, with the same MPS/fidelity floors enforced.
-- This replaces the removed `--voice-sample` style anchor (dropped in 6.0.0): the "sound like me" use case becomes a saved custom persona, not a per-run sample file.
-- **Corpus-distilled quantitative bands**: a `persona new --from-corpus ` path that ingests a multi-document personal corpus and distills per-metric allow-bands (p5/p95) from patina's existing deterministic stylometry (burstiness / MATTR / lexicon density / line rhythm) — promoting a persona from qualitative blocks to a quantitative, verifiable voice fingerprint, optionally segmented by genre/channel. Bands live in `src/features/*` (LLM-free); the persona still cannot lower the MPS/fidelity floors.
-- **Personalized avoided-lexicon**: derive a persona's `avoid` list from the user corpus's zero-occurrence terms (a personal AI-tell dictionary), complementing the generic corpus-grounded AI lexicon.
-- **Holdout validation methodology**: seal ~15% of the user corpus, build bands only on the rest, and verify a distilled persona generalizes to the unseen holdout (plus a corpus manifest/ledger integrity check) before it is offered.
-- Trigger + attribution: a large effort that competes with the payment/launch path — implementation starts only after payment stabilization and a **separate** approval. The genre×channel fingerprint bands, personalized zero-occurrence tells, and holdout/ledger methodology are adopted (idea-level) from `kimsh-1/gn-voice` (MIT — Section A: `scripts/`, `references/fingerprint-slim.json`, `references/ai-tells.json`, `style-profile/`). patina distills only the user's own corpus and never ingests gn-voice's `corpus/`, `analysis/`, or `examples/` (Section B, all rights reserved). Credit gn-voice in `NOTICE` if any Section A structure is reused.
+- **Corpus-distilled quantitative bands**: a separately approved
+ `persona new --from-corpus ` path could derive per-metric allow-bands from
+ the user's own corpus using LLM-free stylometry.
+- **Personalized avoided lexicon**: derive a Persona's avoid list from terms
+ absent from the user's corpus.
+- **Holdout validation**: reserve part of the user's corpus to verify that a
+ derived voice fingerprint generalizes before offering it.
-Acceptance criteria:
-- A user can author, save, and select a custom persona without editing source.
-- Custom personas honor the same meaning-preservation/fidelity hard floors as bundled personas.
-- No regression to the conservative `preserve` default for users who do not author one.
+These larger personalization paths remain gated behind payment stabilization
+and separate approval.
## 3. Community roadmap
diff --git a/docs/agents.md b/docs/agents.md
index b37d838..c5d4f9e 100644
--- a/docs/agents.md
+++ b/docs/agents.md
@@ -18,11 +18,11 @@ Use when: you need a pre-rewrite audit log, want to verify which patterns were a
### patina-fidelity-auditor
-Given the ORIGINAL and REWRITE texts, audits meaning preservation against all four fidelity criteria defined in `core/scoring.md` §§9-14: claims preserved, no fabrication, tone match, and length ratio. Also checks MPS-level semantic anchors: numbers, polarity, causation, named entities, and direct quotes. Returns a PASS or NEEDS-ROLLBACK verdict with the fidelity score and offending spans identified.
+Given the ORIGINAL and REWRITE texts, audits meaning preservation against all four fidelity criteria defined in `core/scoring.md` §§9-14: claims preserved, no fabrication, audience/register match, and length ratio. Also checks MPS-level semantic anchors: numbers, polarity, causation, named entities, and direct quotes. Returns a PASS or NEEDS-ROLLBACK verdict with the fidelity score and offending spans identified.
The verification floor applies here: fidelity_score ≥ `verification.fidelity-floor` (default: 70) is required for PASS.
-Use when: you need an auditable record that the rewrite did not alter facts, or when fidelity is critical (academic, technical, medical, legal profiles).
+Use when: you need an auditable record that the rewrite did not alter facts, or when fidelity is critical for academic, technical, medical, or legal Document Types.
### patina-naturalness-reviewer
diff --git a/docs/integrations/playground.md b/docs/integrations/playground.md
index 43b2aca..e998d49 100644
--- a/docs/integrations/playground.md
+++ b/docs/integrations/playground.md
@@ -33,8 +33,9 @@ never talks directly to a provider in v1).
Deploy the repository root on Vercel so the root `vercel.json` can rewrite `/` to
the chat entry while keeping the chat module graph and brand/social assets
-reachable. The rewrite function bundle must include `patterns/**`, `profiles/**`,
-`core/**`, `lexicon/**`, and `.patina.default.yaml`, and the free tier needs
+reachable. The rewrite function bundle must include `patterns/**`,
+`document-types/**`, `personas/**`, `core/**`, `lexicon/**`, and
+`.patina.default.yaml`, and the free tier needs
`PATINA_FREE_API_KEY` plus `KV_REST_API_URL` / `KV_REST_API_TOKEN` for the
fail-closed quota.
diff --git a/profiles/academic.md b/document-types/academic.md
similarity index 71%
rename from profiles/academic.md
rename to document-types/academic.md
index 55e76a5..9a332a0 100644
--- a/profiles/academic.md
+++ b/document-types/academic.md
@@ -1,8 +1,20 @@
---
-profile: academic
-name: 학술 논문/연구 보고서 프로필
-version: 2.0.0
+document-type: academic
+name: 학술 논문/연구 보고서
+version: 3.0.0
scope: 학술 논문, 연구 보고서, 학회 발표 자료, 석/박사 논문
+purpose: "Present research questions, methods, evidence, limitations, and conclusions with disciplinary precision."
+audience:
+ - "Researchers, reviewers, and technically informed readers"
+structure:
+ - "Preserve the source’s section hierarchy, citations, tables, and method/result boundaries"
+ - "Keep claims adjacent to their evidence and limitations"
+style:
+ - "Use field-standard terminology and calibrated uncertainty"
+ - "Prefer precise attribution over vague authority"
+avoid:
+ - "Inventing citations, evidence, methods, results, or stronger certainty"
+ - "Replacing justified hedging or passive constructions merely to sound conversational"
pattern-overrides:
ko:
18: reduce # 한자어/공식어 — 학술에서는 자연스러운 수준 허용
@@ -22,13 +34,13 @@ pattern-overrides:
16: reduce # 敬語 — 학술 문체에서는 비해당
---
-# 학술 논문/연구 보고서 프로필
+# 학술 논문/연구 보고서
학술 문서의 관행을 존중하면서 AI 패턴을 제거한다. 헤징, 수동태, 격식체 등 학술 글쓰기에서 정당한 요소는 보존한다.
## 범위
-학술 논문, 연구 보고서, 학회 발표 자료, 석/박사 논문. 과학 커뮤니케이션이나 대중 과학 글은 `default` 프로필 사용.
+학술 논문, 연구 보고서, 학회 발표 자료, 석/박사 논문. 과학 커뮤니케이션이나 대중 과학 글은 `default` 문서 유형 사용.
## 적극 교정 대상
diff --git a/profiles/blog.md b/document-types/blog.md
similarity index 69%
rename from profiles/blog.md
rename to document-types/blog.md
index 7c2eca4..a813ece 100644
--- a/profiles/blog.md
+++ b/document-types/blog.md
@@ -1,8 +1,20 @@
---
-profile: blog
-name: 블로그/에세이 프로필
-version: 2.0.0
+document-type: blog
+name: 블로그/에세이
+version: 3.0.0
scope: 개인 블로그, 에세이, 개인 뉴스레터
+purpose: "Publish a readable post or essay with a clear through-line and scannable organization."
+audience:
+ - "Readers who chose the topic but may not share the author’s full context"
+structure:
+ - "Use a useful opening, coherent sections, and an ending earned by the source"
+ - "Keep headings only when they improve navigation"
+style:
+ - "Prefer concrete examples and natural transitions over generic summary scaffolding"
+ - "Preserve the source’s level of technical detail"
+avoid:
+ - "Adding a personal anecdote, opinion, first-person stance, or intimacy absent from the source"
+ - "Turning the post into marketing copy or a rigid listicle"
pattern-overrides:
ko:
14: suppress # 볼드체 — 블로그에서는 흔하게 사용, 교정 불필요
@@ -30,13 +42,13 @@ pattern-overrides:
7: amplify # AI語彙 — 個人文では特に浮くため強めに検出
---
-# 블로그/에세이 프로필
+# 블로그/에세이
-개인 블로그와 에세이에 맞는 교정을 수행한다. 독자와 대화하는 느낌을 살리면서 AI 패턴을 제거한다.
+개인 블로그와 에세이의 읽기 흐름과 탐색 구조를 적용하되, 원문의 목소리는 보존한다. 대화체, 1인칭, 개인적 의견은 Persona나 원문이 제공할 때만 사용한다.
## 범위
-이 프로필은 **개인 블로그와 에세이**에 한정된다. 기업 블로그, 공식 뉴스레터, 보도자료는 이 프로필의 범위가 아니다.
+이 문서 유형은 **개인 블로그와 에세이**에 한정된다. 기업 블로그, 공식 뉴스레터, 보도자료는 범위가 아니다.
## 패턴 처리 (한국어)
@@ -44,11 +56,11 @@ pattern-overrides:
- **이모지(ko #17):** 1-2개 자연스러운 사용은 허용. 모든 항목에 이모지를 붙인 경우만 교정.
- **한자어/공식어(ko #18), ~적 접미사(ko #8):** 블로그에서 "도모하다", "혁신적인" 같은 표현은 특히 부자연스럽다. 적극 교정.
- **구조적 반복(ko #25):** 블로그에서도 모든 단락이 동일 구조면 AI 티가 난다. 적극 교정.
-- **번역체(ko #26):** 블로그는 구어체에 가까워야 하므로 번역체가 더 눈에 띈다. 적극 교정.
+- **번역체(ko #26):** 블로그의 독자 지향적 흐름을 방해하는 직역 표현을 교정한다. 문장 종결과 격식은 Register 또는 원문의 지배 어투를 따른다.
## Pattern Handling (English)
- **Boldface (en #14), Inline-header lists (en #15):** Blogs legitimately use bold and headers for readability. Only correct mechanical over-use across every bullet.
- **Emojis (en #17):** 1–2 natural uses are tolerated. Correct only when every item gets an emoji.
-- **AI vocabulary (en #7):** Words like "delve", "tapestry", "leverage", "multifaceted" are especially jarring in casual blog prose. Aggressively correct.
+- **AI vocabulary (en #7):** Words like "delve", "tapestry", "leverage", and "multifaceted" are especially jarring in reader-facing blog prose. Aggressively correct them without inventing a personal voice.
- **Copula avoidance (en #8):** "Serves as", "functions as" read stiffly in blog writing. Replace with simple "is/are" constructions.
diff --git a/document-types/casual-conversation.md b/document-types/casual-conversation.md
new file mode 100644
index 0000000..30bacb6
--- /dev/null
+++ b/document-types/casual-conversation.md
@@ -0,0 +1,92 @@
+---
+document-type: casual-conversation
+name: 대화/메시지
+version: 3.0.0
+scope: 메신저 대화, 댓글, 채팅형 답변, 짧은 SNS 상호작용
+purpose: "Preserve the intent of a short message, reply, comment, or conversational turn."
+audience:
+ - "The named or implied participants in the source conversation"
+structure:
+ - "Keep turns and paragraphs short enough for the channel"
+ - "Preserve questions, replies, references, and turn-taking context"
+style:
+ - "Use interaction-shaped wording rather than article or report scaffolding"
+ - "Keep the source’s degree of directness"
+avoid:
+ - "Adding familiarity, honorific distance, first-person identity, jokes, or emotion absent from the source"
+ - "Using this Document Type to choose casual/professional delivery; Register owns that choice"
+pattern-overrides:
+ ko:
+ 8: amplify # ~적 접미사 — 짧은 대화에서는 특히 부자연스러움
+ 18: amplify # 한자어/공식어 — 한자어 대신 순한 단어 우선
+ 14: suppress # 볼드체 — SNS/댓글에서는 사용 안 함
+ 19: reduce # 챗봇 표현 — 실제 응대 문맥의 공손 표현은 일부 허용
+ en:
+ 8: amplify # Copula avoidance
+ 7: amplify # AI vocabulary
+ 14: suppress # Boldface
+ zh:
+ 7: amplify # AI高频词 — 亲密对话里“赋能/生态”特别不像人话
+ 18: amplify # 书面/公文体 — 朋友语气中应换成口语
+ 14: suppress # 加粗 — 聊天/SNS语气中不作为AI痕迹处理
+ 19: reduce # 聊天机器人痕迹 — 亲切服务语可少量保留
+ ja:
+ 7: amplify # AI語彙 — 親しい会話では特に不自然
+ 18: amplify # 硬質文体 — 友人向けなら口語へ寄せる
+ 16: amplify # 過剰敬語 — 親密な会話では距離が出るため強めに直す
+ 14: suppress # 太字 — 会話調ではAI判定の主因にしない
+ 19: reduce # チャットボット痕跡 — 親切な一言は一部許容
+---
+
+# 대화/메시지 (`casual-conversation`)
+
+`casual-conversation`은 기존 CLI 식별자를 유지하지만, 이 정책이 정하는 것은
+**대화형 문서의 관습**이다. casual/professional 전달 방식은 `--register`가
+정한다. 이 Document Type만으로 반말, 존댓말, 친밀감, 1인칭 성격을 만들지 않는다.
+
+## 적용 예시
+
+### Input
+> 배포를 진행하기에 앞서 환경 변수 설정이 올바른지 확인하는 것이 필요합니다. 설정이 누락된 경우 인증 오류가 발생할 수 있습니다.
+
+### Document Type만 적용
+> 배포 전에 환경 변수가 올바른지 확인해야 합니다. 누락되면 인증 오류가 날 수 있습니다.
+
+원문의 professional 전달 방식은 그대로 두고, 메시지에 맞게 앞부분과 문장 길이만
+정리한다.
+
+### `--register casual`도 명시
+> 배포 전에 환경 변수부터 확인해. 빠지면 인증 오류가 날 수 있어.
+
+두 번째 변화는 Document Type이 아니라 Register가 소유한다. 어떤 경우에도 새 화자,
+농담, 감정, 친밀한 관계를 덧붙이지 않는다.
+
+## `blog`와의 차이
+
+| 관습 | `blog` | `casual-conversation` |
+|---|---|---|
+| 기본 단위 | 완결된 글/포스트 | 한 메시지·댓글·응답 턴 |
+| 구조 | 도입·본문 섹션·마무리 가능 | 짧은 턴, 질문·답변 맥락 우선 |
+| 참조 | 글 내부 문맥 | 앞선 메시지·상대 발화 참조 |
+| Register | 별도 축 | 별도 축 |
+| Persona | 별도 축 | 별도 축 |
+
+## 사용
+
+```bash
+patina --document-type casual-conversation --lang ko input.txt
+patina --document-type casual-conversation --register casual --lang ko input.txt
+```
+
+또는 `.patina.yaml`에:
+
+```yaml
+document-type: casual-conversation
+register: professional
+```
+
+## 한계
+
+- 원문에 없는 답변 맥락이나 상대 의도를 추측하지 않는다.
+- 기술·법률·의학 문서의 전문 용어와 의미 하한은 메시지 길이에 맞추려고 낮추지 않는다.
+- 번역 결과의 사실·주장 오류는 이 Document Type이 교정하지 않는다.
diff --git a/profiles/code-comment.md b/document-types/code-comment.md
similarity index 79%
rename from profiles/code-comment.md
rename to document-types/code-comment.md
index 3b71741..5dd53d3 100644
--- a/profiles/code-comment.md
+++ b/document-types/code-comment.md
@@ -1,8 +1,20 @@
---
-profile: code-comment
-name: 코드 주석/docstring 프로필
-version: 2.0.0
+document-type: code-comment
+name: 코드 주석/docstring
+version: 3.0.0
scope: 코드 주석, docstring, JSDoc/TSDoc, inline comment, TODO/FIXME 주석
+purpose: "Explain code behavior, constraints, rationale, or follow-up work at the point of use."
+audience:
+ - "Maintainers reading the surrounding code"
+structure:
+ - "Match the host comment or docstring format"
+ - "Put the actionable behavior or constraint first"
+style:
+ - "Use code identifiers and concrete conditions"
+ - "Prefer short statements over prose introductions"
+avoid:
+ - "Restating syntax without explaining behavior or rationale"
+ - "Inventing guarantees, implementation details, issue links, or future work"
pattern-overrides:
ko:
7: amplify # AI 고빈도 어휘 — 주석에서는 특히 소음
@@ -26,15 +38,15 @@ pattern-overrides:
22: amplify # フィラー — 削除優先
---
-# 코드 주석/docstring 프로필 (`code-comment`)
+# 코드 주석/docstring (`code-comment`)
-코드 옆 텍스트는 산문이 아니라 유지보수 단서다. 이 프로필은 주석을 짧게 만들되, **왜 이 코드가 필요한지**, **어떤 edge case를 막는지**, **입출력·불변조건이 무엇인지**를 남긴다.
+코드 옆 텍스트는 산문이 아니라 유지보수 단서다. 이 문서 유형은 주석을 짧게 만들되, **왜 이 코드가 필요한지**, **어떤 edge case를 막는지**, **입출력·불변조건이 무엇인지**를 남긴다.
## 범위
- inline comment, block comment, docstring, JSDoc/TSDoc, TODO/FIXME 주석
-- README, API 문서, 튜토리얼은 `technical` 또는 `instructional` 프로필을 쓴다.
-- 커밋 메시지는 `commit-message`, 릴리스 노트는 `release-notes` 프로필을 쓴다.
+- README, API 문서, 튜토리얼은 `technical` 또는 `instructional` 문서 유형을 쓴다.
+- 커밋 메시지는 `commit-message`, 릴리스 노트는 `release-notes` 문서 유형을 쓴다.
## 적극 교정할 genre tell
diff --git a/profiles/commit-message.md b/document-types/commit-message.md
similarity index 81%
rename from profiles/commit-message.md
rename to document-types/commit-message.md
index 656b891..98f0ec7 100644
--- a/profiles/commit-message.md
+++ b/document-types/commit-message.md
@@ -1,8 +1,20 @@
---
-profile: commit-message
-name: 커밋 메시지 프로필
-version: 2.0.0
+document-type: commit-message
+name: 커밋 메시지
+version: 3.0.0
scope: Git commit subject/body, squash message, revert message, PR squash summary
+purpose: "Record what changed and why for reviewers and future maintainers."
+audience:
+ - "Reviewers, release tooling, and future maintainers"
+structure:
+ - "Keep a concise imperative subject and a body only when rationale or risk needs explanation"
+ - "Preserve trailers, issue references, and breaking-change markers"
+style:
+ - "Name the changed behavior and affected component directly"
+ - "Use repository terminology"
+avoid:
+ - "Marketing language, generic success claims, or implementation detail unsupported by the diff"
+ - "Inventing tests, issues, impacts, or compatibility guarantees"
pattern-overrides:
ko:
7: amplify # AI 고빈도 어휘 — 커밋에서는 특히 모호함
@@ -26,13 +38,13 @@ pattern-overrides:
31: amplify # 結論シグナル — 削除
---
-# 커밋 메시지 프로필 (`commit-message`)
+# 커밋 메시지 (`commit-message`)
커밋 메시지는 변경 diff의 제목이 아니라 미래 디버깅을 위한 결정 기록이다. subject는 **명령형 또는 의도형**으로 짧게 쓰고, body는 제약·검증·기각한 대안을 남긴다.
## 범위
-Git commit subject/body, squash message, revert message, PR squash summary. 릴리스 노트나 사용자-facing changelog는 `release-notes` 프로필을 쓴다.
+Git commit subject/body, squash message, revert message, PR squash summary. 릴리스 노트나 사용자-facing changelog는 `release-notes` 문서 유형을 쓴다.
## 적극 교정할 genre tell
diff --git a/document-types/default.md b/document-types/default.md
new file mode 100644
index 0000000..b3a9a98
--- /dev/null
+++ b/document-types/default.md
@@ -0,0 +1,34 @@
+---
+document-type: default
+name: 기본 문서 유형
+version: 3.0.0
+scope: 일반 텍스트 — 다른 문서 유형이 명시되지 않은 모든 경우. 원문의 의도된 격식과 용도를 유지한다.
+purpose: "Preserve the source document’s function when no narrower document policy is selected."
+audience:
+ - "The source document’s existing audience"
+structure:
+ - "Preserve the source order, hierarchy, and formatting unless an AI pattern requires a local repair"
+ - "Add no genre template, heading scheme, or call to action"
+style:
+ - "Use concrete, readable prose appropriate to the detected source context"
+ - "Keep domain terms that carry meaning"
+avoid:
+ - "Inferring a Persona, casual/professional Register, or new document genre"
+ - "Adding opinions, first-person voice, anecdotes, emotion, or intimacy absent from the source"
+# pattern-overrides 없음 — 모든 패턴을 기본 가중치로 적용한다.
+# 특정 패턴을 강화/억제하려면 적합한 문서 유형(blog/academic/formal 등)을 사용한다.
+---
+
+# 기본 문서 유형
+
+원문의 문서 기능과 지배 어투를 먼저 파악하고, 이를 유지하면서 AI 패턴을 제거한다. 어떤 방향으로도 강제로 끌고 가지 않는다.
+
+## 범위
+
+다른 문서 유형이 명시되지 않은 모든 텍스트에 적용된다. `--document-type blog`나 `--document-type formal`을 주지 않으면 default가 사용된다.
+
+## 패턴 처리
+
+- 모든 패턴을 동일한 우선순위로 처리한다.
+- `blocklist`/`allowlist` 설정이 있으면 그에 따라 조절한다.
+- 특정 패턴을 강화/억제하려면 `--document-type `을 명시한다.
diff --git a/profiles/email.md b/document-types/email.md
similarity index 62%
rename from profiles/email.md
rename to document-types/email.md
index aa11ee2..4cd3e26 100644
--- a/profiles/email.md
+++ b/document-types/email.md
@@ -1,8 +1,20 @@
---
-profile: email
-name: 비즈니스 이메일 프로필
-version: 2.0.0
+document-type: email
+name: 비즈니스 이메일
+version: 3.0.0
scope: 비즈니스 이메일, 공식 서신, 사내 커뮤니케이션
+purpose: "Move a specific request, decision, update, or response through email."
+audience:
+ - "The source message’s recipients and copied stakeholders"
+structure:
+ - "Surface the purpose early, then the minimum context, action, owner, and deadline present in the source"
+ - "Preserve necessary greeting, sign-off, quoted context, and thread references"
+style:
+ - "Use clear requests and explicit ownership"
+ - "Keep etiquette proportional to the existing relationship"
+avoid:
+ - "Adding urgency, commitments, deadlines, recipients, or friendliness not present in the source"
+ - "Letting politeness obscure the requested action"
pattern-overrides:
ko:
18: reduce # 한자어/공식어 — 이메일에서 중간 수준 격식 허용
@@ -11,7 +23,7 @@ pattern-overrides:
en:
18: suppress # Curly quotes — 이메일에서 비해당
21: reduce # Sycophantic — some politeness is email convention
- 8: reduce # Copula avoidance — "serves as" tolerable in formal email
+ 8: reduce # Copula avoidance — some conventional email phrasing is acceptable
zh:
18: reduce # 公文体 — 이메일에서 중간 수준 허용
21: reduce # 谄媚 — 약간의 공손함 허용
@@ -21,13 +33,13 @@ pattern-overrides:
21: reduce # お世辞 — 약간의 공손함 허용
---
-# 비즈니스 이메일 프로필
+# 비즈니스 이메일
-비즈니스 이메일의 적절한 격식과 공손함을 유지하면서 AI 패턴을 제거한다. "예의 바른" 것과 "챗봇 같은" 것의 경계를 지킨다.
+비즈니스 이메일의 수신자 관계, 요청 구조, 인사·서명 관습을 유지하면서 AI 패턴을 제거한다. casual/professional 전달 방식은 Register 또는 원문의 지배 어투를 따른다.
## 범위
-비즈니스 이메일, 공식 서신, 사내 커뮤니케이션. 마케팅 이메일이나 뉴스레터는 `default` 또는 `blog` 프로필 사용.
+비즈니스 이메일, 공식 서신, 사내 커뮤니케이션. 마케팅 이메일이나 뉴스레터는 `default` 또는 `blog` 문서 유형 사용.
## 적극 교정 대상
diff --git a/profiles/formal.md b/document-types/formal.md
similarity index 69%
rename from profiles/formal.md
rename to document-types/formal.md
index 01cf897..78831b0 100644
--- a/profiles/formal.md
+++ b/document-types/formal.md
@@ -1,8 +1,20 @@
---
-profile: formal
-name: 정형 문서 프로필
-version: 2.0.0
+document-type: formal
+name: 정형 문서
+version: 3.0.0
scope: 이력서, 자기소개서, 사업 제안서, 공식 보고서, 커버레터
+purpose: "Present qualifications, proposals, or official findings in a structured, reviewable document."
+audience:
+ - "Recruiters, evaluators, decision-makers, or official stakeholders"
+structure:
+ - "Use stable sections, labels, evidence-bearing bullets, and document-specific metadata"
+ - "Keep parallel structure where it supports scanning and comparison"
+style:
+ - "Use precise role, outcome, scope, and evidence language"
+ - "Prefer verifiable statements over broad self-description"
+avoid:
+ - "Using this Document Type as a formality control; Register owns casual/professional delivery"
+ - "Inventing achievements, metrics, responsibilities, endorsements, or institutional claims"
pattern-overrides:
ko:
25: suppress # 구조적 반복 — 정형 문서는 반복 구조가 정상 (경력 항목, 불릿 리스트)
@@ -29,28 +41,28 @@ pattern-overrides:
8: reduce # 〜的 — 正式文書では一部許容
---
-# 정형 문서 프로필
+# 정형 문서
-이력서, 자기소개서, 사업 제안서 등 격식을 유지해야 하는 문서에 사용한다.
-AI 패턴은 제거하되, 전문적인 어조를 유지한다.
+이력서, 자기소개서, 사업 제안서처럼 정해진 섹션과 비교 가능한 항목이 필요한 문서에 사용한다.
+`formal`은 기존 CLI 식별자일 뿐 formality 축이 아니다. casual/professional 전달 방식은 `--register`가 정한다.
## 범위
-이 프로필은 **격식을 요구하는 전문 문서**에 한정된다:
+이 문서 유형은 **정형 구조와 검토 가능한 근거를 요구하는 문서**에 한정된다:
- 이력서 / CV
- 자기소개서 / 커버레터
- 사업 제안서
- 공식 보고서
- 기업 소개서
-개인 블로그, 에세이, SNS 글은 이 프로필의 범위가 아니다.
+개인 블로그, 에세이, SNS 글은 이 문서 유형의 범위가 아니다.
## 핵심 원칙
-정형 문서의 AI 교정에서 가장 흔한 실수는 **과도한 캐주얼화**다.
-개인적 목소리를 앞세우는 지침은 블로그나 에세이에는 적절하지만, 이력서에 적용하면 전문성을 해친다.
-
-이 프로필은 AI 패턴 제거와 격식 유지를 양립시킨다.
+정형 문서의 항목 구조, 레이블, 증거 배치, 병렬성은 Document Type이 정한다.
+문장 종결과 casual/professional 전달 방식은 Register가 정하고, 고유한 어휘·리듬은
+명시적 Persona가 정한다. Document Type만으로 전문적인 화자나 격식을 새로 만들지
+않는다.
## 패턴 처리 (한국어)
diff --git a/profiles/instructional.md b/document-types/instructional.md
similarity index 62%
rename from profiles/instructional.md
rename to document-types/instructional.md
index b77d9c2..3dbb602 100644
--- a/profiles/instructional.md
+++ b/document-types/instructional.md
@@ -1,42 +1,54 @@
---
-profile: instructional
-name: 인스트럭셔널/하우투 프로필
-version: 2.0.0
+document-type: instructional
+name: 인스트럭셔널/하우투
+version: 3.0.0
scope: 튜토리얼, 하우투 가이드, 단계별 설명, 기술 문서, 교육 콘텐츠
+purpose: "Help a reader complete a task safely and verify the result."
+audience:
+ - "Readers with the prerequisite knowledge stated or implied by the source"
+structure:
+ - "Use prerequisites, ordered steps, commands, expected results, and recovery notes where the source supports them"
+ - "Keep warnings immediately before the risky action"
+style:
+ - "Use direct, executable instructions and exact identifiers"
+ - "Separate required actions from optional context"
+avoid:
+ - "Inventing commands, flags, prerequisites, results, or safety guarantees"
+ - "Replacing necessary sequence with narrative prose"
pattern-overrides:
ko:
- 25: allow # 번호 목록 구조 — 인스트럭셔널에서는 필수 허용
+ 25: suppress # 번호 목록 구조 — 인스트럭셔널에서는 필수 허용
22: reduce # 필러 관용구 — 지시문에서 불필요한 완충 표현 제거
28: suppress # 과도한 한정 표현 — 지시문에서 명확성 저해
- 15: allow # 인라인 헤더 — 단계 구분에 유용
+ 15: suppress # 인라인 헤더 — 단계 구분에 유용
14: reduce # 볼드체 — 핵심 명령어에만 허용, 과도한 볼드 교정
en:
- 25: allow # Numbered structure — essential for instructional content
+ 25: suppress # Numbered structure — essential for instructional content
22: reduce # Filler idioms — reduce padding in instructional prose
28: suppress # Over-qualifying — clarity requires commitment
- 15: allow # Inline-header lists — useful for step delineation
+ 15: suppress # Inline-header lists — useful for step delineation
14: reduce # Boldface — allow for key terms/commands, correct overuse
zh:
- 25: allow # 结构/编号 — 教程需要清晰步骤,编号结构必须保留
+ 25: suppress # 结构/编号 — 教程需要清晰步骤,编号结构必须保留
22: reduce # 填充表达 — 指令文里减少铺垫和寒暄
23: amplify # 过度弱化 — 步骤说明需要明确,不要层层对冲
- 15: allow # 内联标题 — 可用于步骤/参数分区
+ 15: suppress # 内联标题 — 可用于步骤/参数分区
14: reduce # 加粗 — 命令、文件名、关键提醒可加粗,滥用才纠正
ja:
- 25: allow # 構造/番号 — 手順説明では番号構造が必要
+ 25: suppress # 構造/番号 — 手順説明では番号構造が必要
22: reduce # フィラー — 手順では前置きや緩衝表現を減らす
23: amplify # 過剰ヘッジ — 手順は明確さを優先する
- 15: allow # インラインヘッダー — 手順やパラメータ整理に有用
+ 15: suppress # インラインヘッダー — 手順やパラメータ整理に有用
14: reduce # 太字 — コマンド/ファイル名/注意点の強調は許容
---
-# 인스트럭셔널/하우투 프로필
+# 인스트럭셔널/하우투
튜토리얼과 단계별 가이드에 맞는 교정을 수행한다. 독자가 지시를 따를 수 있도록 명확하고 행동 지향적인 문장을 만드는 것이 핵심이다.
## 범위
-이 프로필은 **튜토리얼, 하우투 가이드, 단계별 설명, 기술 문서, 교육 콘텐츠**에 적합하다. 개인 에세이, 뉴스 기사, 마케팅 카피는 이 프로필의 범위가 아니다.
+이 문서 유형은 **튜토리얼, 하우투 가이드, 단계별 설명, 기술 문서, 교육 콘텐츠**에 적합하다. 개인 에세이, 뉴스 기사, 마케팅 카피는 범위가 아니다.
## 패턴 처리
diff --git a/profiles/legal.md b/document-types/legal.md
similarity index 75%
rename from profiles/legal.md
rename to document-types/legal.md
index 00b69eb..838611b 100644
--- a/profiles/legal.md
+++ b/document-types/legal.md
@@ -1,8 +1,20 @@
---
-profile: legal
-name: 법률 문서 프로필
-version: 2.0.0
+document-type: legal
+name: 법률 문서
+version: 3.0.0
scope: 계약서, 법률 의견서, 판결문, 약관, 법률 보고서
+purpose: "State rights, duties, definitions, conditions, exceptions, and procedures without changing legal effect."
+audience:
+ - "Parties, counsel, regulators, or readers governed by the text"
+structure:
+ - "Preserve numbering, defined-term scope, cross-references, conditions, exceptions, and obligation hierarchy"
+ - "Keep each modifier attached to the clause it qualifies"
+style:
+ - "Use consistent defined terms and jurisdiction-appropriate legal language"
+ - "Retain deliberate repetition when it prevents ambiguity"
+avoid:
+ - "Modernizing wording when it changes scope, obligation, permission, prohibition, or remedy"
+ - "Inventing authority, definitions, exceptions, dates, parties, or legal advice"
pattern-overrides:
ko:
18: suppress # 한자어/공식어 — 법률 문서의 표준 어휘
@@ -26,13 +38,13 @@ pattern-overrides:
27: reduce # ている — 법률 문서에서 일부 허용
---
-# 법률 문서 프로필
+# 법률 문서
법률 문서의 관행적 표현을 보존하면서 AI 패턴을 제거한다. 수동태, 명사화, 격식체 등 법률 글쓰기의 표준 요소는 건드리지 않는다.
## 범위
-계약서, 법률 의견서, 판결문, 약관, 법률 보고서. 법률 블로그나 법률 뉴스 기사는 `default` 또는 `blog` 프로필 사용.
+계약서, 법률 의견서, 판결문, 약관, 법률 보고서. 법률 블로그나 법률 뉴스 기사는 `default` 또는 `blog` 문서 유형 사용.
## 적극 교정 대상
diff --git a/profiles/marketing.md b/document-types/marketing.md
similarity index 73%
rename from profiles/marketing.md
rename to document-types/marketing.md
index 23ab150..1ed3885 100644
--- a/profiles/marketing.md
+++ b/document-types/marketing.md
@@ -1,8 +1,20 @@
---
-profile: marketing
-name: 마케팅/광고 프로필
-version: 2.0.0
+document-type: marketing
+name: 마케팅/광고
+version: 3.0.0
scope: 광고 카피, 마케팅 콘텐츠, 제품 소개, 랜딩 페이지, 프레스 릴리즈
+purpose: "Communicate a supported offer, benefit, proof point, and next action to a defined audience."
+audience:
+ - "The source campaign’s intended prospects or customers"
+structure:
+ - "Connect audience problem, supported value, evidence, and call to action without adding missing claims"
+ - "Keep required campaign, channel, and compliance elements"
+style:
+ - "Prefer specific benefits, mechanisms, and proof over generic excitement"
+ - "Make the requested action unambiguous"
+avoid:
+ - "Inventing scarcity, urgency, testimonials, guarantees, metrics, or product capabilities"
+ - "Strengthening comparative, causal, or performance claims"
pattern-overrides:
ko:
4: reduce # 홍보성 언어 — 마케팅에서는 일부 허용, 과도한 것만 교정
@@ -24,13 +36,13 @@ pattern-overrides:
14: reduce # 太字 — 허용
---
-# 마케팅/광고 프로필
+# 마케팅/광고
마케팅 콘텐츠의 설득력을 유지하면서 AI가 생성한 뻔한 패턴을 제거한다. 홍보성 언어는 마케팅의 본질이므로 일정 수준 허용하되, AI가 찍어낸 듯한 제네릭 카피는 교정한다.
## 범위
-광고 카피, 마케팅 콘텐츠, 제품 소개, 랜딩 페이지, 프레스 릴리즈. 기업 블로그는 `blog` 프로필이 더 적합.
+광고 카피, 마케팅 콘텐츠, 제품 소개, 랜딩 페이지, 프레스 릴리즈. 기업 블로그는 `blog` 문서 유형이 더 적합.
## 적극 교정 대상
diff --git a/profiles/medical.md b/document-types/medical.md
similarity index 71%
rename from profiles/medical.md
rename to document-types/medical.md
index 1725c69..04f4fd3 100644
--- a/profiles/medical.md
+++ b/document-types/medical.md
@@ -1,8 +1,20 @@
---
-profile: medical
-name: 의료/의학 문서 프로필
-version: 2.0.0
+document-type: medical
+name: 의료/의학 문서
+version: 3.0.0
scope: 임상 보고서, 의학 논문, 진료 가이드라인, 약물 정보, 환자 교육 자료
+purpose: "Communicate clinical, research, or patient information without changing medical meaning or uncertainty."
+audience:
+ - "The clinicians, researchers, patients, or caregivers identified by the source"
+structure:
+ - "Preserve evidence, population, intervention, outcome, risk, limitation, and recommendation boundaries"
+ - "Keep warnings and contraindications prominent"
+style:
+ - "Use audience-appropriate medical terminology with calibrated uncertainty"
+ - "Distinguish observation, association, recommendation, and causation"
+avoid:
+ - "Inventing diagnoses, treatments, dosages, evidence, contraindications, or reassurance"
+ - "Converting uncertainty into certainty or general information into personal medical advice"
pattern-overrides:
ko:
23: reduce # 헤징 — 의학적 불확실성 ("~일 수 있다") 허용
@@ -22,13 +34,13 @@ pattern-overrides:
16: reduce # 敬語 — 환자 교육 자료에서 일부 허용
---
-# 의료/의학 문서 프로필
+# 의료/의학 문서
의학 문서의 엄밀성과 불확실성 표현을 보존하면서 AI 패턴을 제거한다. 의학적 헤징, 수동태, 전문 용어는 건드리지 않는다.
## 범위
-임상 보고서, 의학 논문, 진료 가이드라인, 약물 정보, 환자 교육 자료. 건강 블로그나 대중 건강 기사는 `default` 프로필 사용.
+임상 보고서, 의학 논문, 진료 가이드라인, 약물 정보, 환자 교육 자료. 건강 블로그나 대중 건강 기사는 `default` 문서 유형 사용.
## 적극 교정 대상
diff --git a/profiles/namuwiki.md b/document-types/namuwiki.md
similarity index 78%
rename from profiles/namuwiki.md
rename to document-types/namuwiki.md
index 750f05e..0480eba 100644
--- a/profiles/namuwiki.md
+++ b/document-types/namuwiki.md
@@ -1,10 +1,22 @@
---
-profile: namuwiki
-name: 나무위키풍 프로필
-version: 2.0.0
+document-type: namuwiki
+name: 나무위키풍
+version: 3.0.0
scope: 한국어 위키풍 설명, 인터넷 커뮤니티식 부연, 가벼운 리뷰/해설 글. ko 전용이며 실제 나무위키 문서 텍스트를 포함하지 않는다.
language: ko
license-note: Original style description only; do not copy CC BY-NC-SA NamuWiki article text into this repo.
+purpose: "Explain a topic in an original Korean community-wiki format without copying source-site text."
+audience:
+ - "Korean readers familiar with community-wiki navigation and asides"
+structure:
+ - "Use concise lead context, topic sections, lists, and bounded asides supported by the source"
+ - "Preserve source-backed caveats and cross-references without imitating a specific article"
+style:
+ - "Use original Korean internet-explanatory phrasing and concrete topic terminology"
+ - "Keep asides subordinate to factual explanation"
+avoid:
+ - "Copying or closely paraphrasing NamuWiki article text"
+ - "Inventing trivia, controversy, citations, community consensus, jokes, or speaker identity"
pattern-overrides:
ko:
# current schema: suppress=allow this marker, reduce=only fix overuse, amplify=fix aggressively
@@ -29,9 +41,9 @@ pattern-overrides:
19: suppress # 챗봇 응대 표현은 나무위키풍으로 바꾸기보다 제거
---
-# 나무위키풍 프로필
+# 나무위키풍
-이 프로필은 한국어 글을 **위키풍 해설 + 인터넷식 사족** 쪽으로 기울인다. 목적은 실제 나무위키 문서를 모사하거나 복제하는 것이 아니라, 딱딱한 AI 문체를 한국어 인터넷 독자가 익숙하게 읽는 설명체로 낮추는 것이다.
+한국어 글을 **위키풍 해설 + 인터넷식 사족** 쪽으로 기울인다. 목적은 실제 나무위키 문서를 모사하거나 복제하는 것이 아니라, 딱딱한 AI 문체를 한국어 인터넷 독자가 익숙하게 읽는 설명체로 낮추는 것이다.
## 라이선스 가드레일
@@ -88,7 +100,7 @@ pattern-overrides:
## 사용
```bash
-patina --lang ko --profile namuwiki input.txt
+patina --lang ko --document-type namuwiki input.txt
```
-`--lang en|zh|ja`와 함께 쓰면 기본 프로필로 폴백한다. 이 프로필은 한국어 조사, 어미, 인터넷식 부연을 전제로 하기 때문이다.
+`--lang en|zh|ja`와 함께 쓰면 기본 문서 유형으로 폴백한다. 한국어 조사, 어미, 인터넷식 부연을 전제로 하기 때문이다.
diff --git a/profiles/narrative.md b/document-types/narrative.md
similarity index 73%
rename from profiles/narrative.md
rename to document-types/narrative.md
index a9809e6..48fc48f 100644
--- a/profiles/narrative.md
+++ b/document-types/narrative.md
@@ -1,8 +1,20 @@
---
-profile: narrative
-name: 내러티브/에세이 프로필
-version: 2.0.0
+document-type: narrative
+name: 내러티브/에세이
+version: 3.0.0
scope: 1인칭 에세이, 개인 서사, 회고록, 경험 기반 글쓰기
+purpose: "Preserve a source narrative’s events, perspective, sequence, and emotional logic while removing AI residue."
+audience:
+ - "The source narrative’s intended readers"
+structure:
+ - "Preserve chronology or deliberate nonlinearity, scene boundaries, point of view, and reveal order"
+ - "Let moments carry meaning instead of appending generic lessons"
+style:
+ - "Use concrete scene details already supported by the source"
+ - "Vary pacing only where the source permits"
+avoid:
+ - "Inventing scenes, dialogue, sensory detail, motives, emotions, or a moral"
+ - "Changing narrator identity, point of view, chronology, or factual status"
pattern-overrides:
ko:
26: reduce # 번역체 — 내러티브에서 구어체 허용
@@ -30,13 +42,13 @@ pattern-overrides:
25: suppress # 構造的繰り返し — 番号/テンプレ段落は個人叙事に不向き
---
-# 내러티브/에세이 프로필
+# 내러티브/에세이
1인칭 서사와 경험 기반 에세이에 맞는 교정을 수행한다. 화자의 목소리와 시간 흐름, 감정적 진실이 글의 핵심이다.
## 범위
-이 프로필은 **1인칭 에세이, 개인 서사, 회고록, 경험 기반 글쓰기**에 적합하다. 학술 논문, 기업 보고서, 뉴스 기사는 이 프로필의 범위가 아니다.
+이 문서 유형은 **1인칭 에세이, 개인 서사, 회고록, 경험 기반 글쓰기**에 적합하다. 학술 논문, 기업 보고서, 뉴스 기사는 범위가 아니다.
## 패턴 처리
diff --git a/profiles/release-notes.md b/document-types/release-notes.md
similarity index 83%
rename from profiles/release-notes.md
rename to document-types/release-notes.md
index 1861838..f5febf7 100644
--- a/profiles/release-notes.md
+++ b/document-types/release-notes.md
@@ -1,8 +1,20 @@
---
-profile: release-notes
-name: 릴리스 노트 프로필
-version: 2.0.0
+document-type: release-notes
+name: 릴리스 노트
+version: 3.0.0
scope: changelog, release notes, GitHub Releases, npm/GHCR release announcement
+purpose: "Explain shipped changes, user impact, compatibility risk, and required action."
+audience:
+ - "Users, operators, integrators, and maintainers of the released product"
+structure:
+ - "Group changes by user impact and surface breaking changes, migration steps, and known limits"
+ - "Preserve versions, identifiers, links, and commands"
+style:
+ - "Lead with observable change and consequence"
+ - "Use product terminology consistently"
+avoid:
+ - "Inventing shipped behavior, compatibility, fixes, migration steps, or availability"
+ - "Replacing concrete impact with launch excitement"
pattern-overrides:
ko:
14: reduce # 볼드체 — 릴리스 노트 heading/emphasis 일부 허용
@@ -26,13 +38,13 @@ pattern-overrides:
25: reduce # 箇条書き構造 — リリースノートでは標準
---
-# 릴리스 노트 프로필 (`release-notes`)
+# 릴리스 노트 (`release-notes`)
릴리스 노트는 마케팅 카피와 운영 공지의 중간이다. 사용자가 궁금해하는 것은 "무엇이 바뀌었나"보다 **내가 무엇을 해야 하나**, **무엇이 깨질 수 있나**, **왜 업그레이드해야 하나**다.
## 범위
-CHANGELOG 항목, GitHub Releases, npm/GHCR 릴리스 공지, 버전 업그레이드 안내. 내부 커밋 메시지는 `commit-message`, 긴 기술 설명은 `technical` 프로필을 쓴다.
+CHANGELOG 항목, GitHub Releases, npm/GHCR 릴리스 공지, 버전 업그레이드 안내. 내부 커밋 메시지는 `commit-message`, 긴 기술 설명은 `technical` 문서 유형을 쓴다.
## 적극 교정할 genre tell
diff --git a/profiles/social.md b/document-types/social.md
similarity index 55%
rename from profiles/social.md
rename to document-types/social.md
index 295fa64..0c031c7 100644
--- a/profiles/social.md
+++ b/document-types/social.md
@@ -1,8 +1,20 @@
---
-profile: social
-name: SNS/소셜미디어 프로필
-version: 2.0.0
+document-type: social
+name: SNS/소셜미디어
+version: 3.0.0
scope: 트위터/X, 인스타그램, 스레드, 블루스카이, 카카오스토리, 페이스북
+purpose: "Deliver one source-backed point within a social platform’s reading and formatting constraints."
+audience:
+ - "The source post’s intended followers or community"
+structure:
+ - "Put the point early and keep only context needed to understand or act on it"
+ - "Preserve handles, links, hashtags, thread order, and platform syntax when meaningful"
+style:
+ - "Use compact, concrete wording suited to the source platform"
+ - "Keep the source’s call to action and emphasis"
+avoid:
+ - "Inventing hooks, outrage, intimacy, authority, virality, scarcity, or engagement bait"
+ - "Using platform conventions to strengthen unsupported claims"
pattern-overrides:
ko:
17: suppress # 이모지 — SNS에서는 표준
@@ -11,12 +23,12 @@ pattern-overrides:
21: reduce # 아첨 — 약간의 친근한 표현은 허용
en:
17: suppress # Emojis — standard in social media
- 22: reduce # Filler — some casual filler is natural
+ 22: reduce # Filler — some platform-native connective wording is natural
14: suppress # Boldface — not relevant for most social platforms
- 21: reduce # Sycophantic — some friendly tone allowed
+ 21: reduce # Sycophantic — distinguish audience acknowledgment from flattery
zh:
17: suppress # 表情符号 — SNS 표준
- 22: reduce # 填充表达 — 캐주얼 필러 허용
+ 22: reduce # 填充表达 — 平台原生的少量连接语可保留
14: suppress # 加粗 — 비해당
ja:
17: suppress # 絵文字 — SNS 표준
@@ -24,13 +36,13 @@ pattern-overrides:
14: suppress # 太字 — 비해당
---
-# SNS/소셜미디어 프로필
+# SNS/소셜미디어
-소셜미디어 특유의 캐주얼한 어조를 유지하면서 AI 패턴을 제거한다. 이모지, 구어체, 파편 문장을 허용하되, 여전히 AI 티가 나는 구조적 패턴은 교정한다.
+소셜미디어의 짧은 단위, 링크·핸들·해시태그, 플랫폼 서식을 보존하면서 AI 패턴을 제거한다. 문장 종결과 casual/professional 전달 방식은 Register 또는 원문의 지배 어투를 따른다.
## 범위
-트위터/X, 인스타그램, 스레드, 블루스카이, 카카오스토리, 페이스북 포스트. 기업 공식 계정은 `default` 프로필이 더 적합.
+트위터/X, 인스타그램, 스레드, 블루스카이, 카카오스토리, 페이스북 포스트. 기업 공식 계정은 `default` 문서 유형이 더 적합.
## 적극 교정 대상
diff --git a/profiles/technical.md b/document-types/technical.md
similarity index 69%
rename from profiles/technical.md
rename to document-types/technical.md
index fad6efd..90c553a 100644
--- a/profiles/technical.md
+++ b/document-types/technical.md
@@ -1,8 +1,20 @@
---
-profile: technical
-name: 기술 문서/API 문서 프로필
-version: 2.0.0
+document-type: technical
+name: 기술 문서/API 문서
+version: 3.0.0
scope: 기술 문서, API 문서, README, 가이드, 튜토리얼, 사양서
+purpose: "Help readers understand, use, integrate, operate, or troubleshoot a technical system."
+audience:
+ - "Developers, operators, integrators, or technical users identified by the source"
+structure:
+ - "Preserve API shapes, code blocks, commands, parameters, prerequisites, warnings, and expected results"
+ - "Order concepts by dependency and procedures by execution sequence"
+style:
+ - "Use exact identifiers and define unfamiliar terms before use"
+ - "Prefer mechanisms and constraints over broad benefits"
+avoid:
+ - "Inventing behavior, defaults, compatibility, commands, outputs, or guarantees"
+ - "Changing code, identifiers, links, version numbers, or normative requirement strength"
pattern-overrides:
ko:
14: suppress # 볼드체 — 기술 문서에서 키워드/파라미터 볼드 표준
@@ -23,13 +35,13 @@ pattern-overrides:
16: suppress # 敬語 — 기술 문서에서는 비해당 (である調 기본)
---
-# 기술 문서/API 문서 프로필
+# 기술 문서/API 문서
기술 문서의 형식적 관행을 존중하면서 AI 패턴을 제거한다. 볼드 키워드, 파라미터 테이블, 인라인 헤더 리스트 등 기술 문서의 표준 요소는 건드리지 않는다.
## 범위
-API 문서, README, 가이드, 튜토리얼, 기술 사양서. 기술 블로그 글은 `blog` 프로필이 더 적합.
+API 문서, README, 가이드, 튜토리얼, 기술 사양서. 기술 블로그 글은 `blog` 문서 유형이 더 적합.
## 적극 교정 대상
diff --git a/examples/README.md b/examples/README.md
index d81f91d..d40de91 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -5,7 +5,19 @@
## 목적
패턴이 의도한 대로 동작하는지 (성공), 의도치 않게 오탐/과교정하는지 (실패) 두 가지 케이스를 각 패턴별로 문서화한다.
-프로필 전용 예시는 `examples/profiles/`에 둔다. 이 예시는 패턴 번호보다 register/voice 보존을 설명하며, 외부 라이선스가 있는 원문을 복사하지 않는다.
+Document Type 전용 예시는 `examples/document-types/`에 둔다. 이 예시는 패턴 번호보다 문서 정책과 구조 관습을 설명하며, 외부 라이선스가 있는 원문을 복사하지 않는다.
+
+## 재작성 3축 예시
+
+재작성 입력은 서로 독립적인 세 축으로 나뉜다.
+
+| 축 | 예시 | 바꾸는 것 | 바꾸지 않는 것 |
+|---|---|---|---|
+| Document Type | [`document-types/`](document-types/), [`rewrite-axes/academic-ko.md`](rewrite-axes/academic-ko.md) | 장르·용도·구조 관습·패턴 정책 | 목소리, casual/professional 전달 방식, 사실 |
+| Persona | `patina --persona ` | 사용자가 명시적으로 고른 재사용 목소리 | 장르, Register, 사실, 검증 하한 |
+| Register | [`rewrite-axes/casual-ko.md`](rewrite-axes/casual-ko.md), [`rewrite-axes/professional-ko.md`](rewrite-axes/professional-ko.md) | `casual | professional` 전달 방식 | 장르, Persona, 사실 |
+
+Persona와 Register를 생략하면 원문의 목소리와 레지스터를 보존한다. 모든 before/after는 주장·수치·극성·인과를 그대로 유지해야 하며, Document Type이나 Register를 바꾼다는 이유로 인물·장면·성과·사례를 새로 만들 수 없다.
## 파일 명명 규칙
diff --git a/examples/profiles/namuwiki-01-after.md b/examples/document-types/namuwiki-01-after.md
similarity index 84%
rename from examples/profiles/namuwiki-01-after.md
rename to examples/document-types/namuwiki-01-after.md
index 98c3e10..7d3a05b 100644
--- a/examples/profiles/namuwiki-01-after.md
+++ b/examples/document-types/namuwiki-01-after.md
@@ -1,11 +1,11 @@
---
-profile: namuwiki
+document-type: namuwiki
type: after
language: ko
name: 도구 소개
license: original
---
-# NamuWiki profile example 1 — after
+# NamuWiki Document Type example 1 — after
작업 문장을 한 번 훑어서 AI 티 나는 부분을 표시해 주는 도구다. 생산성을 올린다고 크게 말할 수도 있겠지만, 실제로는 `혁신적인`, `체계적인` 같은 말부터 지우는 쪽에 가깝다.[* 그래서 결과물이 더 수수해진다. 이게 장점이다.]
diff --git a/examples/profiles/namuwiki-01-before.md b/examples/document-types/namuwiki-01-before.md
similarity index 78%
rename from examples/profiles/namuwiki-01-before.md
rename to examples/document-types/namuwiki-01-before.md
index 49b47c6..d6dccea 100644
--- a/examples/profiles/namuwiki-01-before.md
+++ b/examples/document-types/namuwiki-01-before.md
@@ -1,11 +1,11 @@
---
-profile: namuwiki
+document-type: namuwiki
type: before
language: ko
name: 도구 소개
license: original
---
-# NamuWiki profile example 1 — before
+# NamuWiki Document Type example 1 — before
이 솔루션은 혁신적인 접근을 통해 사용자의 생산성을 극대화하고, 체계적인 워크플로우를 기반으로 지속 가능한 가치를 제공합니다.
diff --git a/examples/document-types/namuwiki-02-after.md b/examples/document-types/namuwiki-02-after.md
new file mode 100644
index 0000000..ba9d28c
--- /dev/null
+++ b/examples/document-types/namuwiki-02-after.md
@@ -0,0 +1,11 @@
+---
+document-type: namuwiki
+type: after
+language: ko
+name: 기능 설명
+license: original
+---
+
+# NamuWiki Document Type example 2 — after
+
+hosted playground는 서버에서 재작성과 점수를 처리하고, CLI와 editor skill은 사용자가 고른 backend를 쓴다. 실행 경로가 달라 처음엔 헷갈릴 수 있지만, 어떤 경로로 텍스트가 전송되는지는 문서에 구분돼 있다.
diff --git a/examples/profiles/namuwiki-02-before.md b/examples/document-types/namuwiki-02-before.md
similarity index 77%
rename from examples/profiles/namuwiki-02-before.md
rename to examples/document-types/namuwiki-02-before.md
index 4a9ffd5..75d8cc0 100644
--- a/examples/profiles/namuwiki-02-before.md
+++ b/examples/document-types/namuwiki-02-before.md
@@ -1,11 +1,11 @@
---
-profile: namuwiki
+document-type: namuwiki
type: before
language: ko
name: 기능 설명
license: original
---
-# NamuWiki profile example 2 — before
+# NamuWiki Document Type example 2 — before
본 기능은 다양한 사용 환경에서 효과적으로 활용될 수 있으며, 향후 사용자 경험 향상에 중요한 역할을 할 것으로 기대됩니다.
diff --git a/examples/profiles/namuwiki-02-after.md b/examples/profiles/namuwiki-02-after.md
deleted file mode 100644
index 428e6d5..0000000
--- a/examples/profiles/namuwiki-02-after.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-profile: namuwiki
-type: after
-language: ko
-name: 기능 설명
-license: original
----
-
-# NamuWiki profile example 2 — after
-
-브라우저에서는 점수만 보고, 실제 재작성은 CLI나 에디터 스킬에서 한다. 역할이 나뉘어 있어서 처음엔 조금 헷갈릴 수 있다. 대신 웹에 글을 맡기는 구조가 아니라는 점은 꽤 명확하다.
diff --git a/examples/tones/RESULTS.md b/examples/rewrite-axes/RESULTS.md
similarity index 71%
rename from examples/tones/RESULTS.md
rename to examples/rewrite-axes/RESULTS.md
index 8a913da..797caea 100644
--- a/examples/tones/RESULTS.md
+++ b/examples/rewrite-axes/RESULTS.md
@@ -1,6 +1,8 @@
-# Tone Categorization — Actual Run Results
+# Rewrite-Axis Categorization — Historical Run Results
-Real outputs from patina v3.10.0 with `codex-cli` backend (gpt-5.5).
+Recorded outputs from patina v3.10.0 with `codex-cli` (gpt-5.5), remapped to
+the v7 axis names. Reproduce `casual`/`professional` with `--register`; reproduce
+`academic`/`narrative`/`marketing`/`instructional` with `--document-type`.
**Date:** 2026-05-06
**Backend:** codex-cli (Codex v0.128.0, gpt-5.5)
@@ -59,23 +61,6 @@ Real outputs from patina v3.10.0 with `codex-cli` backend (gpt-5.5).
---
-## auto (`--tone auto`)
-
-> 이 도구는 개발자가 반복적인 코드를 처리하는 시간을 줄여준다. 비슷한 패턴의 함수나 설정 파일을 만들 때 특히 효과가 크고, 업무 환경도 크게 가리지 않는다. 사용법도 복잡하지 않아 초보 개발자도 비교적 빨리 익힐 수 있다.
-
-**Tone resolution footer:**
-
-```yaml
-tone: professional
-tone_source: auto
-tone_evidence: ["no signal cluster reached threshold; residual default"]
-tone_confidence: low
-```
-
-Note: with no explicit tone signal in the input, auto-detection falls back to `professional` (`tone_confidence: low`). The `tone_evidence` field surfaces the fallback reason, so callers can detect "no signal" vs. "strong signal" cases programmatically.
-
----
-
## AI-likeness Scores (`--score`, codex-cli backend)
| Variant | Overall (±10) | Verdict |
@@ -94,8 +79,7 @@ All rewritten outputs score at or near 0/100 on the model's scoring rubric, and
## Observations
-- All 6 named tones preserve the core meaning: productivity gain, repetitive task reduction, versatility, low learning curve.
-- Style changes are tone-appropriate: casual uses informal speech, academic is objective, narrative adds first-person experience, marketing uses impact sentences, instructional uses imperatives.
-- casual and instructional outputs add concrete examples (CRUD 뼈대, 테스트 초안, 보일러플레이트) that aren't in the input. This is intentional tone-fitting elaboration, not factual drift — the underlying claim ("repetitive code work") is preserved.
-- YAML footer is emitted correctly for every named tone with `tone_source: user` and `tone_confidence: high`. For `--tone auto` with no detected signal, the footer reports `tone_source: auto`, `tone_confidence: low`, and a `tone_evidence` reason.
+- All six explicit variants preserve the core meaning: productivity gain, repetitive task reduction, versatility, low learning curve.
+- Delivery and document-policy changes differ: casual/professional use Register; academic/narrative/marketing/instructional use Document Type.
+- casual and instructional outputs add concrete examples (CRUD 뼈대, 테스트 초안, 보일러플레이트) that are not in the input. Under the v7 contract, new factual examples are not justified by an axis change and should be rejected by meaning verification.
- `--score` shows all variants in the human band (0–3), but see the caveats above before treating the absolute numbers as a quality measure.
diff --git a/examples/rewrite-axes/academic-en.md b/examples/rewrite-axes/academic-en.md
new file mode 100644
index 0000000..d8cb79e
--- /dev/null
+++ b/examples/rewrite-axes/academic-en.md
@@ -0,0 +1,24 @@
+# Fixture: Document Type `academic` × en
+
+**diff focus:** Objective observation, hedged claims, filler adjectives removed
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--document-type academic`)
+
+The tool may improve developer productivity, with the clearest potential benefit in repetitive code-generation tasks. It also appears applicable across varied work environments. Its relatively gentle learning curve may support adoption by developers with different levels of experience.
+
+---
+
+## Diff notes
+
+- Preserved the source's tentative claim strength with "may" and "appears."
+- Removed unsupported emphasis while retaining the repetitive-task focus.
+- Kept applicability and learning-curve claims separate for clarity.
+- Added no deployment evidence, adoption-cost claim, or research caveat absent from the source.
diff --git a/examples/rewrite-axes/academic-ko.md b/examples/rewrite-axes/academic-ko.md
new file mode 100644
index 0000000..c018acf
--- /dev/null
+++ b/examples/rewrite-axes/academic-ko.md
@@ -0,0 +1,24 @@
+# Fixture: Document Type `academic` × ko
+
+**diff focus:** 객관적 서술, 과잉 수식어 제거, 원문의 유보 수준 보존
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--document-type academic`)
+
+해당 도구는 개발자 생산성을 개선할 가능성이 있으며, 그 효과는 반복적 코드 작성 작업에서 가장 뚜렷하게 나타날 수 있다. 다양한 업무 환경에 적용할 수 있고 학습 곡선도 비교적 완만해, 경험 수준이 다른 개발자의 접근을 도울 수 있다.
+
+---
+
+## Diff notes
+
+- "가능성"과 "수 있다"로 원문의 유보 수준을 보존
+- 과장 수식은 줄이고 반복 작업이라는 초점을 유지
+- 적용 범위와 학습 곡선 주장을 분리해 명확히 서술
+- 원문에 없는 배포 근거, 비용, 연구 한계를 추가하지 않음
diff --git a/examples/rewrite-axes/casual-en.md b/examples/rewrite-axes/casual-en.md
new file mode 100644
index 0000000..f55b78c
--- /dev/null
+++ b/examples/rewrite-axes/casual-en.md
@@ -0,0 +1,23 @@
+# Fixture: Register `casual` × en
+
+**diff focus:** Contractions and plain wording without adding a first-person witness
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--register casual`)
+
+This tool can make repetitive code work easier. It seems useful across different work environments, and the learning curve isn't too steep, even if you're new to it.
+
+---
+
+## Diff notes
+
+- Replaced formal abstractions with plain, conversational wording.
+- Used a contraction and direct address without inventing personal experience.
+- Preserved the source's tentative strength, scope, and accessibility claim.
diff --git a/examples/rewrite-axes/casual-ko.md b/examples/rewrite-axes/casual-ko.md
new file mode 100644
index 0000000..fd17c63
--- /dev/null
+++ b/examples/rewrite-axes/casual-ko.md
@@ -0,0 +1,23 @@
+# Fixture: Register `casual` × ko
+
+**diff focus:** 구어체와 쉬운 표현, 1인칭 경험은 새로 만들지 않음
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--register casual`)
+
+이 도구는 반복 코드를 짤 때 생산성을 높이는 데 도움이 될 수 있어요. 여러 업무 환경에 적용할 수 있고, 배우기도 비교적 쉬워서 경험이 적어도 접근하기 어렵지 않아요.
+
+---
+
+## Diff notes
+
+- 딱딱한 추상어를 구어체로 바꾸되 1인칭 경험은 추가하지 않음
+- 가능성을 확정적 성과로 강화하지 않음
+- 생산성, 반복 작업, 적용 범위, 학습 곡선이라는 원문의 주장만 보존
diff --git a/examples/rewrite-axes/instructional-en.md b/examples/rewrite-axes/instructional-en.md
new file mode 100644
index 0000000..6a22291
--- /dev/null
+++ b/examples/rewrite-axes/instructional-en.md
@@ -0,0 +1,27 @@
+# Fixture: Document Type `instructional` × en
+
+**diff focus:** Direct declarative sentences, structured list, original claim strength preserved
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--document-type instructional`)
+
+This tool may improve developer productivity.
+
+- **Primary use:** Repetitive code-generation tasks, where its benefits are most evident.
+- **Work environments:** Applicable across varied work environments.
+- **Adoption:** A relatively gentle learning curve makes it accessible to developers at different experience levels.
+
+---
+
+## Diff notes
+
+- Recast the same four claims as a scannable instructional list.
+- Removed promotional filler without strengthening "potential" into a guarantee.
+- Preserved the source's scope; no task examples, environments, or prerequisites were invented.
diff --git a/examples/rewrite-axes/instructional-ko.md b/examples/rewrite-axes/instructional-ko.md
new file mode 100644
index 0000000..d6a77ad
--- /dev/null
+++ b/examples/rewrite-axes/instructional-ko.md
@@ -0,0 +1,27 @@
+# Fixture: Document Type `instructional` × ko
+
+**diff focus:** 설명형 문장, 구조적 나열, 원문의 유보 수준 보존
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--document-type instructional`)
+
+이 도구는 개발자 생산성을 높이는 데 도움이 될 수 있습니다.
+
+- **주요 용도:** 효과가 가장 두드러지는 반복 코드 작성
+- **업무 환경:** 다양한 환경에 적용 가능
+- **도입:** 비교적 완만한 학습 곡선으로 경험 수준과 관계없이 접근 가능
+
+---
+
+## Diff notes
+
+- 같은 네 가지 주장을 훑어보기 쉬운 설명형 목록으로 재구성
+- 가능성을 보장으로 강화하지 않고 과도한 수식만 제거
+- 원문에 없는 작업 예시, 환경, 사전 지식을 추가하지 않음
diff --git a/examples/rewrite-axes/marketing-en.md b/examples/rewrite-axes/marketing-en.md
new file mode 100644
index 0000000..f61c3bb
--- /dev/null
+++ b/examples/rewrite-axes/marketing-en.md
@@ -0,0 +1,24 @@
+# Fixture: Document Type `marketing` × en
+
+**diff focus:** Short impact sentences, concrete benefits, CTA-friendly
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--document-type marketing`)
+
+Spend less time on repetitive code generation. This tool can improve developer productivity across varied work environments, and its gentle learning curve makes it accessible to developers at every experience level.
+
+---
+
+## Diff notes
+
+- Opened with the source's main benefit in landing-page form.
+- Compressed the same productivity, repetitive-task, applicability, and learning-curve claims.
+- Kept "can improve" rather than turning the source's potential into a guarantee.
+- Added no stack, schedule, performance number, or unsupported CTA claim.
diff --git a/examples/rewrite-axes/marketing-ko.md b/examples/rewrite-axes/marketing-ko.md
new file mode 100644
index 0000000..279911e
--- /dev/null
+++ b/examples/rewrite-axes/marketing-ko.md
@@ -0,0 +1,23 @@
+# Fixture: Document Type `marketing` × ko
+
+**diff focus:** 짧은 임팩트 문장, 구체적 혜택, CTA 친화적 구조
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--document-type marketing`)
+
+반복 코드 작성에 쓰는 시간을 줄여보세요. 이 도구는 다양한 업무 환경에서 개발자 생산성을 높이는 데 도움이 될 수 있고, 학습 곡선이 완만해 경험 수준과 관계없이 쉽게 시작할 수 있습니다.
+
+---
+
+## Diff notes
+
+- 원문의 핵심 혜택을 랜딩 페이지형 첫 문장으로 배치
+- 생산성, 반복 작업, 적용 범위, 학습 곡선이라는 네 주장만 압축
+- 가능성을 보장으로 바꾸지 않고, 스택·기간·성과 수치를 새로 만들지 않음
diff --git a/examples/rewrite-axes/narrative-en.md b/examples/rewrite-axes/narrative-en.md
new file mode 100644
index 0000000..c3b78ab
--- /dev/null
+++ b/examples/rewrite-axes/narrative-en.md
@@ -0,0 +1,23 @@
+# Fixture: Document Type `narrative` × en
+
+**diff focus:** Narrative progression without invented people, scenes, or results
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--document-type narrative`)
+
+Repetitive code generation is where the difference appears first. From there, the tool can fit into varied work environments, and its gentle learning curve keeps it accessible to developers with different levels of experience. That is where its potential productivity benefit comes from.
+
+---
+
+## Diff notes
+
+- Reordered the four source claims into a beginning-to-end progression.
+- Used no first-person witness, team, date, duration, or measured result absent from the source.
+- Preserved "potential" instead of converting it into a factual success story.
diff --git a/examples/rewrite-axes/narrative-ko.md b/examples/rewrite-axes/narrative-ko.md
new file mode 100644
index 0000000..e7441f9
--- /dev/null
+++ b/examples/rewrite-axes/narrative-ko.md
@@ -0,0 +1,23 @@
+# Fixture: Document Type `narrative` × ko
+
+**diff focus:** 인물·장면·성과를 새로 만들지 않는 서사적 진행
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--document-type narrative`)
+
+차이는 반복 코드를 작성할 때 먼저 드러납니다. 이 도구는 거기서 출발해 다양한 업무 환경에 적용할 수 있고, 학습 곡선도 비교적 완만해 경험이 적은 개발자에게도 열려 있습니다. 생산성 향상 가능성은 이 흐름에서 나옵니다.
+
+---
+
+## Diff notes
+
+- 네 가지 원문 주장을 시작과 전개가 있는 순서로 재배치
+- 원문에 없는 1인칭 목격자, 팀, 날짜, 기간, 측정 성과를 추가하지 않음
+- 생산성 "가능성"을 실제 성공담으로 강화하지 않음
diff --git a/examples/rewrite-axes/professional-en.md b/examples/rewrite-axes/professional-en.md
new file mode 100644
index 0000000..eb9bce8
--- /dev/null
+++ b/examples/rewrite-axes/professional-en.md
@@ -0,0 +1,23 @@
+# Fixture: Register `professional` × en
+
+**diff focus:** Clear professional delivery, filler adjectives removed, claim strength preserved
+
+---
+
+## Input
+
+This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
+
+---
+
+## Output (`--register professional`)
+
+This tool can improve developer productivity, particularly in repetitive code-generation tasks. It is applicable across varied work environments, and its relatively gentle learning curve makes it accessible to developers at different experience levels.
+
+---
+
+## Diff notes
+
+- Removed filler adjectives while keeping the source's professional delivery.
+- Preserved the same productivity, repetitive-task, applicability, and accessibility claims.
+- Added no stack, integration, onboarding-cost, or performance claim.
diff --git a/examples/rewrite-axes/professional-ko.md b/examples/rewrite-axes/professional-ko.md
new file mode 100644
index 0000000..4f4d4aa
--- /dev/null
+++ b/examples/rewrite-axes/professional-ko.md
@@ -0,0 +1,23 @@
+# Fixture: Register `professional` × ko
+
+**diff focus:** 격식체 유지하되 공허한 수식어 제거, 명확하고 간결한 문장
+
+---
+
+## Input
+
+이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
+
+---
+
+## Output (`--register professional`)
+
+이 도구는 반복적인 코드 작성에서 개발자 생산성을 높이는 데 도움이 될 수 있습니다. 다양한 업무 환경에 적용할 수 있으며, 학습 곡선이 비교적 완만해 경험 수준과 관계없이 접근하기 쉽습니다.
+
+---
+
+## Diff notes
+
+- 공허한 수식어를 제거하면서 professional 전달 방식을 유지
+- 생산성, 반복 작업, 적용 범위, 접근성이라는 원문의 주장만 보존
+- 팀 규모, 업종, 도입 속도 같은 새 주장을 추가하지 않음
diff --git a/examples/tones/academic-en.md b/examples/tones/academic-en.md
deleted file mode 100644
index 9f2165e..0000000
--- a/examples/tones/academic-en.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Fixture: academic × en
-
-**diff focus:** Objective observation, hedged claims, filler adjectives removed
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (academic tone applied)
-
-The tool appears to improve developer productivity, primarily by automating repetitive code generation. It has been deployed across multiple work environments, suggesting broad applicability. Initial adoption costs are low, which may facilitate uptake among less experienced developers, though long-term productivity effects remain to be measured.
-
----
-
-## Diff notes
-
-- "has demonstrated considerable potential" → "appears to improve" (hedged observation)
-- "particularly evident" → removed (unsupported emphasis)
-- "noteworthy" → "suggesting broad applicability" (qualified claim)
-- "enables developers... with ease" → "may facilitate uptake" (academic hedge)
-- Added closing caveat on long-term effects (scholarly caution)
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/academic-ko.md b/examples/tones/academic-ko.md
deleted file mode 100644
index fa6d385..0000000
--- a/examples/tones/academic-ko.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Fixture: academic × ko
-
-**diff focus:** 객관적 관찰 톤, 과잉 수식어 제거, 유보적 표현
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (academic 톤 적용)
-
-해당 도구는 반복적 코드 작성 작업에서 개발자 생산성을 개선하는 것으로 관찰된다. 복수의 업무 환경에서 적용 가능하며, 초기 학습 비용이 낮다는 점에서 경험이 적은 개발자의 접근성이 높은 것으로 보인다. 다만 장기적 생산성 효과에 대해서는 추가 검증이 필요하다.
-
----
-
-## Diff notes
-
-- "상당한 기여를 하고 있습니다" → "개선하는 것으로 관찰된다" (관찰 기반 표현)
-- "두드러지게 나타나고 있으며" → 삭제 (과장 수식 제거)
-- "활용 가능성이 높습니다" → "적용 가능하며" (단정 완화)
-- "쉽게 접근할 수 있다는 장점이 있습니다" → "접근성이 높은 것으로 보인다" (유보적)
-- 마지막 문장 추가: 학술적 유보 ("추가 검증이 필요") — 원문의 단정적 결론 완화
-- 핵심 의미(생산성, 반복 작업, 범용성, 낮은 진입장벽) 전부 보존
diff --git a/examples/tones/casual-en.md b/examples/tones/casual-en.md
deleted file mode 100644
index 53a7b3c..0000000
--- a/examples/tones/casual-en.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Fixture: casual × en
-
-**diff focus:** Contractions, first-person, informal register
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (casual tone applied)
-
-Honestly, this thing is pretty fast for repetitive code. Works across different setups too, and I've seen people who'd never used it pick it up in an afternoon. It's not magic, but for the boring stuff it gets the job done.
-
----
-
-## Diff notes
-
-- "demonstrated considerable potential" → "is pretty fast" (direct, informal)
-- "Its effectiveness is particularly evident" → removed (redundant with next clause)
-- "applicability across various work environments" → "works across different setups" (casual phrasing)
-- "relatively gentle learning curve" → "pick it up in an afternoon" (concrete)
-- "Furthermore" → removed (AI transition marker)
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/casual-ko.md b/examples/tones/casual-ko.md
deleted file mode 100644
index 602e904..0000000
--- a/examples/tones/casual-ko.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: casual × ko
-
-**diff focus:** 1인칭 도입, 구어체 표현, 축약/반말 투
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (casual 톤 적용)
-
-솔직히 이거 써보면 반복 코드 짤 때 확 빨라지는 게 느껴져요. 이것저것 다른 환경에서도 잘 돌아가고, 처음 쓰는 사람도 금방 적응합니다. 꽤 쓸만해요.
-
----
-
-## Diff notes
-
-- "상당한 기여를 하고 있습니다" → "확 빨라지는 게 느껴져요" (체감 표현으로 전환)
-- "두드러지게 나타나고 있으며" → 삭제 (과잉 수식 제거)
-- "다양한 업무 환경에서 활용 가능성이 높습니다" → "이것저것 다른 환경에서도 잘 돌아가고" (구어체)
-- "학습 곡선이 비교적 완만한 편이어서" → "처음 쓰는 사람도 금방 적응" (구체적 묘사)
-- 핵심 의미(생산성, 반복 작업, 범용성, 낮은 진입장벽) 전부 보존
diff --git a/examples/tones/instructional-en.md b/examples/tones/instructional-en.md
deleted file mode 100644
index 88e9c8b..0000000
--- a/examples/tones/instructional-en.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Fixture: instructional × en
-
-**diff focus:** Direct declarative sentences, structured list, hedging removed
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (instructional tone applied)
-
-This tool automates repetitive code generation to reduce development time.
-
-- **Repetitive tasks**: Generates boilerplate, CRUD patterns, and scaffolding code.
-- **Environment-agnostic**: Works with frontend, backend, and data pipeline setups.
-- **Low barrier to entry**: No prior experience required to start.
-
----
-
-## Diff notes
-
-- "demonstrated considerable potential" → "automates repetitive code generation" (direct statement)
-- "particularly evident" / "noteworthy" → removed (filler)
-- Prose → bullet list (information structured for scanning)
-- "relatively gentle learning curve" → "no prior experience required" (hedging removed)
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/instructional-ko.md b/examples/tones/instructional-ko.md
deleted file mode 100644
index e32488c..0000000
--- a/examples/tones/instructional-ko.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Fixture: instructional × ko
-
-**diff focus:** 명령형/설명형 문장, 유보 표현 제거, 구조적 나열
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (instructional 톤 적용)
-
-이 도구는 반복적인 코드 작성을 자동화하여 개발 시간을 단축합니다.
-
-- **반복 코드 절감**: 보일러플레이트, CRUD 패턴 등 반복 작업을 처리합니다.
-- **환경 무관**: 프론트엔드, 백엔드, 데이터 파이프라인 등 어디서든 사용할 수 있습니다.
-- **낮은 진입 장벽**: 별도 사전 지식 없이 바로 시작할 수 있습니다.
-
----
-
-## Diff notes
-
-- "상당한 기여를 하고 있습니다" → "개발 시간을 단축합니다" (직접적 설명)
-- "효율성이 두드러지게 나타나고" → 삭제 (과잉 수식 제거)
-- 산문 → 불릿 목록 (정보 구조화)
-- "활용 가능성이 높습니다" → 구체적 예시 나열
-- "비교적 완만한 편이어서" → "별도 사전 지식 없이" (유보 표현 제거)
-- 핵심 의미(생산성, 반복 작업, 범용성, 낮은 진입장벽) 전부 보존
diff --git a/examples/tones/marketing-en.md b/examples/tones/marketing-en.md
deleted file mode 100644
index e9507fd..0000000
--- a/examples/tones/marketing-en.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: marketing × en
-
-**diff focus:** Short impact sentences, concrete benefits, CTA-friendly
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (marketing tone applied)
-
-Stop writing the same code twice. This tool handles repetitive generation across any stack, and your team can start using it today — no ramp-up needed. Ship faster.
-
----
-
-## Diff notes
-
-- "demonstrated considerable potential for enhancing" → "Stop writing the same code twice" (impact opener)
-- "applicability across various work environments" → "across any stack" (concise benefit)
-- "gentle learning curve" → "no ramp-up needed" (benefit framing)
-- "Furthermore" → removed; replaced with short closing CTA ("Ship faster")
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/marketing-ko.md b/examples/tones/marketing-ko.md
deleted file mode 100644
index 7daab79..0000000
--- a/examples/tones/marketing-ko.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: marketing × ko
-
-**diff focus:** 짧은 임팩트 문장, 구체적 혜택, CTA 친화적 구조
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (marketing 톤 적용)
-
-반복 코드, 이제 직접 안 짜도 됩니다. 어떤 스택이든 바로 적용 가능하고, 신입 개발자도 첫날부터 쓸 수 있습니다. 개발 속도가 달라집니다.
-
----
-
-## Diff notes
-
-- "상당한 기여를 하고 있습니다" → "이제 직접 안 짜도 됩니다" (임팩트 문장)
-- "다양한 업무 환경에서 활용 가능성" → "어떤 스택이든 바로 적용" (구체적 혜택)
-- "학습 곡선이 완만한 편" → "신입 개발자도 첫날부터" (비유 → 구체적 시나리오)
-- 마지막 문장: 짧은 임팩트 클로징
-- 핵심 의미(생산성, 반복 작업, 범용성, 낮은 진입장벽) 전부 보존
diff --git a/examples/tones/narrative-en.md b/examples/tones/narrative-en.md
deleted file mode 100644
index 5f1e68a..0000000
--- a/examples/tones/narrative-en.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: narrative × en
-
-**diff focus:** First-person experience, concrete scene, sensory detail
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (narrative tone applied)
-
-The first time I ran it, three hours of boilerplate vanished into a single command. I showed it to the frontend team on a Monday; by Wednesday the backend folks were using it too. Even the intern who'd joined two weeks earlier figured it out without asking anyone. Small thing, but the afternoons started feeling a little lighter.
-
----
-
-## Diff notes
-
-- "demonstrated considerable potential" → "three hours of boilerplate vanished" (specific scene)
-- "applicability across various work environments" → "frontend → backend" (narrative progression)
-- "gentle learning curve" → "the intern figured it out" (character detail)
-- "enhancing developer productivity" → "afternoons started feeling lighter" (experiential)
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/narrative-ko.md b/examples/tones/narrative-ko.md
deleted file mode 100644
index 49ff4e5..0000000
--- a/examples/tones/narrative-ko.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: narrative × ko
-
-**diff focus:** 1인칭 경험 서사, 구체적 장면, 감각적 디테일
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (narrative 톤 적용)
-
-처음 써본 날, 매번 복붙하던 보일러플레이트 코드가 한 번에 나와서 멍하니 화면을 봤다. 팀에서 프론트엔드 쪽에 먼저 붙여봤는데, 백엔드 팀도 슬슬 쓰기 시작했다. 입사한 지 한 달 된 후배도 어렵지 않게 쓰더라. 대단한 건 아닌데, 매일 하는 일이 조금씩 가벼워지는 느낌이다.
-
----
-
-## Diff notes
-
-- "생산성 향상에 상당한 기여" → "보일러플레이트가 한 번에 나와서 멍하니" (구체적 경험)
-- "다양한 업무 환경" → "프론트엔드 → 백엔드" (실제 장면)
-- "초보 개발자들도 쉽게" → "입사한 지 한 달 된 후배도" (인물 구체화)
-- "효율성이 두드러지게" → "매일 하는 일이 조금씩 가벼워지는" (체감 묘사)
-- 핵심 의미(생산성, 반복 작업, 범용성, 낮은 진입장벽) 전부 보존
diff --git a/examples/tones/professional-en.md b/examples/tones/professional-en.md
deleted file mode 100644
index 59eb8ef..0000000
--- a/examples/tones/professional-en.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: professional × en
-
-**diff focus:** Formal but concise, filler adjectives removed, clear statements
-
----
-
-## Input
-
-This tool has demonstrated considerable potential for enhancing developer productivity. Its effectiveness is particularly evident in repetitive code generation tasks, and its applicability across various work environments is noteworthy. Furthermore, the relatively gentle learning curve enables developers at all experience levels to adopt it with ease.
-
----
-
-## Output (professional tone applied)
-
-This tool reduces time spent on repetitive code generation, improving overall development speed. It integrates into a range of tech stacks and workflows, and its low onboarding cost makes it accessible to junior and senior developers alike.
-
----
-
-## Diff notes
-
-- "demonstrated considerable potential for enhancing" → "reduces time spent on" (specific outcome)
-- "particularly evident" / "noteworthy" → removed (filler adjectives)
-- "applicability across various work environments" → "integrates into a range of tech stacks" (concrete)
-- "relatively gentle learning curve" → "low onboarding cost" (business-appropriate phrasing)
-- Core meaning (productivity, repetitive tasks, versatility, low barrier) fully preserved
diff --git a/examples/tones/professional-ko.md b/examples/tones/professional-ko.md
deleted file mode 100644
index 169641d..0000000
--- a/examples/tones/professional-ko.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Fixture: professional × ko
-
-**diff focus:** 격식체 유지하되 공허한 수식어 제거, 명확하고 간결한 문장
-
----
-
-## Input
-
-이 도구는 개발자들의 생산성 향상에 상당한 기여를 하고 있습니다. 특히 반복적인 코드 작성 작업에서 그 효율성이 두드러지게 나타나고 있으며, 다양한 업무 환경에서 활용 가능성이 높습니다. 아울러 학습 곡선이 비교적 완만한 편이어서 초보 개발자들도 쉽게 접근할 수 있다는 장점이 있습니다.
-
----
-
-## Output (professional 톤 적용)
-
-이 도구는 반복적인 코드 작성을 줄여 개발 속도를 높입니다. 팀 규모나 업종에 관계없이 적용할 수 있으며, 진입 장벽이 낮아 신규 인원도 빠르게 활용할 수 있습니다.
-
----
-
-## Diff notes
-
-- "상당한 기여를 하고 있습니다" → "개발 속도를 높입니다" (구체화)
-- "그 효율성이 두드러지게 나타나고 있으며" → 삭제 (중복 수식 제거)
-- "다양한 업무 환경에서 활용 가능성이 높습니다" → "팀 규모나 업종에 관계없이 적용" (명확화)
-- "학습 곡선이 비교적 완만한 편이어서" → "진입 장벽이 낮아" (간결화)
-- 핵심 의미 전부 보존, 격식체 유지
diff --git a/package.json b/package.json
index 8d818bb..f95a314 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,7 @@
"src/",
"core/",
"patterns/",
- "profiles/",
+ "document-types/",
"lexicon/",
"personas/",
"docs/",
diff --git a/personas/en/blog-essay.md b/personas/en/blog-essay.md
index 4f1551f..2a4db44 100644
--- a/personas/en/blog-essay.md
+++ b/personas/en/blog-essay.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: blog-essay
name: Personal blog essay (English)
lang: en
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -45,7 +34,6 @@ blocks:
- texture
- weight
- footing
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 1
explanation_habits:
active: true
@@ -59,7 +47,6 @@ blocks:
- unsupported sweeping generalizations
sentence_structure:
active: true
- register: mixed
sentence_length_cv_target: [0.55, 0.95]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.65
diff --git a/personas/en/natural-en.md b/personas/en/natural-en.md
index a036ffe..2662a39 100644
--- a/personas/en/natural-en.md
+++ b/personas/en/natural-en.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: natural-en
name: Plain English (AI-tell stripped)
lang: en
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -58,7 +47,6 @@ blocks:
preferred_metaphors:
active: true
allow: []
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -75,7 +63,6 @@ blocks:
- recommendation overload (you should, you must, be sure to)
sentence_structure:
active: true
- register: plain
sentence_length_cv_target: [0.5, 0.9]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.55
diff --git a/personas/en/preserve.md b/personas/en/preserve.md
deleted file mode 100644
index 19e779a..0000000
--- a/personas/en/preserve.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-schema: patina.persona.v1
-id: preserve
-name: Meaning-preserving default
-lang: en
-source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
-blocks:
- preferred_words:
- active: false
- allow: []
- avoid: []
- density:
- target_per_1000_tokens: 0
- max_per_paragraph: 0
- preferred_metaphors:
- active: false
- allow: []
- forbid_new_facts: true
- max_new_metaphors_per_500_chars: 0
- explanation_habits:
- active: false
- moves: []
- avoid: []
- sentence_structure:
- active: false
- worldview:
- active: false
-target_features: {}
----
-
-# Meaning-preserving default
-
-This body is docs-only. It describes the meaning-preserving default persona and is NOT included in the execution prompt.
diff --git a/personas/en/technical-explainer.md b/personas/en/technical-explainer.md
index 9bebd0a..f52ac2e 100644
--- a/personas/en/technical-explainer.md
+++ b/personas/en/technical-explainer.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: technical-explainer
name: Technical explainer (English)
lang: en
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -41,7 +30,6 @@ blocks:
preferred_metaphors:
active: true
allow: []
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -56,7 +44,6 @@ blocks:
- mechanical enumeration where prose is clearer
sentence_structure:
active: true
- register: plain
sentence_length_cv_target: [0.45, 0.85]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.55
diff --git a/personas/ja/blog-essay.md b/personas/ja/blog-essay.md
index 3f725fa..12ebcfe 100644
--- a/personas/ja/blog-essay.md
+++ b/personas/ja/blog-essay.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: blog-essay
name: 個人ブログのエッセイ(日本語)
lang: ja
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -44,7 +33,6 @@ blocks:
- 温度
- 手ざわり
- 足あと
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 1
explanation_habits:
active: true
@@ -58,7 +46,6 @@ blocks:
- 根拠のない一般化
sentence_structure:
active: true
- register: mixed
sentence_length_cv_target: [0.55, 0.95]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.6
diff --git a/personas/ja/natural-ja.md b/personas/ja/natural-ja.md
index 46fe37b..c805328 100644
--- a/personas/ja/natural-ja.md
+++ b/personas/ja/natural-ja.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: natural-ja
name: 素朴な日本語(AIっぽさ除去)
lang: ja
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -48,7 +37,6 @@ blocks:
preferred_metaphors:
active: true
allow: []
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -65,7 +53,6 @@ blocks:
- 勧奨の乱用(〜しましょう、〜すべきです の繰り返し)
sentence_structure:
active: true
- register: plain
sentence_length_cv_target: [0.5, 0.9]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.5
diff --git a/personas/ja/preserve.md b/personas/ja/preserve.md
deleted file mode 100644
index f23a7fa..0000000
--- a/personas/ja/preserve.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-schema: patina.persona.v1
-id: preserve
-name: 原文の意味を保持
-lang: ja
-source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
-blocks:
- preferred_words:
- active: false
- allow: []
- avoid: []
- density:
- target_per_1000_tokens: 0
- max_per_paragraph: 0
- preferred_metaphors:
- active: false
- allow: []
- forbid_new_facts: true
- max_new_metaphors_per_500_chars: 0
- explanation_habits:
- active: false
- moves: []
- avoid: []
- sentence_structure:
- active: false
- worldview:
- active: false
-target_features: {}
----
-
-# 原文の意味を保持
-
-この本文はドキュメント専用です。原文の意味を保持するデフォルトペルソナの説明であり、実行プロンプトには含まれません。
diff --git a/personas/ko/blog-essay.md b/personas/ko/blog-essay.md
index 2573fed..86d9f09 100644
--- a/personas/ko/blog-essay.md
+++ b/personas/ko/blog-essay.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: blog-essay
name: 개인 블로그 에세이
lang: ko
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -26,7 +15,6 @@ blocks:
preferred_metaphors:
active: true
allow: [길목, 온도, 결, 숨, 발자국]
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 1
explanation_habits:
active: true
@@ -34,7 +22,6 @@ blocks:
avoid: [논문식 서론, 과장된 결론, 근거 없는 일반화]
sentence_structure:
active: true
- register: mixed_plain_polite
sentence_length_cv_target: [0.55, 0.95]
avg_sentence_eojeol_target: [7, 20]
paragraph_sentence_count_target: [2, 5]
@@ -48,8 +35,6 @@ target_features:
lexicon_density_preferred: { target: 9.0, tolerance: 5.0, weight: 0.10 }
lexicon_density_avoid: { target: 0.0, tolerance: 1.0, weight: 0.14 }
sentence_opener_diversity: { target: 0.68, tolerance: 0.18, weight: 0.10 }
- ko_register_plain_ratio: { target: 0.50, tolerance: 0.25, weight: 0.08 }
- ko_register_polite_ratio: { target: 0.30, tolerance: 0.25, weight: 0.08 }
comma_per_sentence: { target: 0.8, tolerance: 0.5, weight: 0.06 }
suffix_class_diversity: { target: 0.48, tolerance: 0.18, weight: 0.08 }
over_edit_churn: { max: 0.45, weight: 0.06 }
diff --git a/personas/ko/natural-ko.md b/personas/ko/natural-ko.md
index 13a6dbd..d3713be 100644
--- a/personas/ko/natural-ko.md
+++ b/personas/ko/natural-ko.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: natural-ko
name: 담백한 한국어 (AI 티 제거)
lang: ko
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -51,7 +40,6 @@ blocks:
preferred_metaphors:
active: true
allow: []
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -68,7 +56,6 @@ blocks:
- 권고형 남발(~하시기 바랍니다·~해야 합니다 반복)
sentence_structure:
active: true
- register: plain
sentence_length_cv_target: [0.5, 0.9]
avg_sentence_eojeol_target: [8, 20]
paragraph_sentence_count_target: [2, 5]
@@ -80,7 +67,6 @@ target_features:
mattr: { target: 0.68, tolerance: 0.12, weight: 0.14 }
lexicon_density_avoid: { target: 0.0, tolerance: 1.0, weight: 0.20 }
sentence_opener_diversity: { target: 0.60, tolerance: 0.20, weight: 0.12 }
- ko_register_plain_ratio: { target: 0.6, tolerance: 0.25, weight: 0.1 }
comma_per_sentence: { target: 0.7, tolerance: 0.5, weight: 0.08 }
suffix_class_diversity: { target: 0.42, tolerance: 0.18, weight: 0.08 }
over_edit_churn: { max: 0.6, weight: 0.08 }
diff --git a/personas/ko/pragmatic-founder.md b/personas/ko/pragmatic-founder.md
index fe728b5..82286ed 100644
--- a/personas/ko/pragmatic-founder.md
+++ b/personas/ko/pragmatic-founder.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: pragmatic-founder
name: 실전형 창업자
lang: ko
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -26,7 +15,6 @@ blocks:
preferred_metaphors:
active: true
allow: [병목, 레버, 안전장치]
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 1
explanation_habits:
active: true
@@ -34,7 +22,6 @@ blocks:
avoid: [장황한 배경 설명, 양쪽 모두 맞다는 식의 회피]
sentence_structure:
active: true
- register: mixed_plain_polite
sentence_length_cv_target: [0.45, 0.85]
avg_sentence_eojeol_target: [8, 18]
paragraph_sentence_count_target: [2, 5]
@@ -48,8 +35,6 @@ target_features:
lexicon_density_preferred: { target: 8.0, tolerance: 5.0, weight: 0.10 }
lexicon_density_avoid: { target: 0.0, tolerance: 1.0, weight: 0.14 }
sentence_opener_diversity: { target: 0.60, tolerance: 0.20, weight: 0.10 }
- ko_register_plain_ratio: { target: 0.55, tolerance: 0.25, weight: 0.08 }
- ko_register_polite_ratio: { target: 0.35, tolerance: 0.25, weight: 0.08 }
comma_per_sentence: { target: 0.7, tolerance: 0.5, weight: 0.06 }
suffix_class_diversity: { target: 0.42, tolerance: 0.18, weight: 0.08 }
over_edit_churn: { max: 0.45, weight: 0.06 }
diff --git a/personas/ko/preserve.md b/personas/ko/preserve.md
deleted file mode 100644
index 45f869d..0000000
--- a/personas/ko/preserve.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-schema: patina.persona.v1
-id: preserve
-name: 원문 의미 보존
-lang: ko
-source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
-blocks:
- preferred_words:
- active: false
- allow: []
- avoid: []
- density:
- target_per_1000_tokens: 0
- max_per_paragraph: 0
- preferred_metaphors:
- active: false
- allow: []
- forbid_new_facts: true
- max_new_metaphors_per_500_chars: 0
- explanation_habits:
- active: false
- moves: []
- avoid: []
- sentence_structure:
- active: false
- worldview:
- active: false
-target_features: {}
----
-
-# 원문 의미 보존
-
-이 body는 docs-only다. 원문 의미 보존 기본 페르소나 설명이며 실행 prompt에는 포함되지 않는다.
diff --git a/personas/ko/soft-professional.md b/personas/ko/soft-professional.md
index 042dbe7..1dce689 100644
--- a/personas/ko/soft-professional.md
+++ b/personas/ko/soft-professional.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: soft-professional
name: 부드러운 업무 문체
lang: ko
source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -26,7 +15,6 @@ blocks:
preferred_metaphors:
active: true
allow: [정리, 방향, 흐름]
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -34,7 +22,6 @@ blocks:
avoid: [과한 격식어, 압박성 표현, 모호한 요청]
sentence_structure:
active: true
- register: polite_professional
sentence_length_cv_target: [0.35, 0.75]
avg_sentence_eojeol_target: [8, 18]
paragraph_sentence_count_target: [1, 4]
@@ -48,8 +35,6 @@ target_features:
lexicon_density_preferred: { target: 7.0, tolerance: 4.0, weight: 0.10 }
lexicon_density_avoid: { target: 0.0, tolerance: 1.0, weight: 0.14 }
sentence_opener_diversity: { target: 0.58, tolerance: 0.18, weight: 0.10 }
- ko_register_plain_ratio: { target: 0.10, tolerance: 0.18, weight: 0.08 }
- ko_register_polite_ratio: { target: 0.75, tolerance: 0.18, weight: 0.08 }
comma_per_sentence: { target: 0.5, tolerance: 0.4, weight: 0.08 }
suffix_class_diversity: { target: 0.38, tolerance: 0.16, weight: 0.08 }
over_edit_churn: { max: 0.40, weight: 0.06 }
diff --git a/personas/ko/technical-explainer.md b/personas/ko/technical-explainer.md
index e2b8957..a207f02 100644
--- a/personas/ko/technical-explainer.md
+++ b/personas/ko/technical-explainer.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: technical-explainer
name: 기술 설명형
lang: ko
source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -26,7 +15,6 @@ blocks:
preferred_metaphors:
active: true
allow: [파이프라인, 경계, 레이어]
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -34,7 +22,6 @@ blocks:
avoid: [원문에 없는 예시 추가, 용어 치환, 결론만 제시]
sentence_structure:
active: true
- register: polite_explanatory
sentence_length_cv_target: [0.35, 0.70]
avg_sentence_eojeol_target: [9, 21]
paragraph_sentence_count_target: [2, 4]
@@ -48,8 +35,6 @@ target_features:
lexicon_density_preferred: { target: 7.0, tolerance: 4.0, weight: 0.10 }
lexicon_density_avoid: { target: 0.0, tolerance: 1.0, weight: 0.14 }
sentence_opener_diversity: { target: 0.55, tolerance: 0.18, weight: 0.10 }
- ko_register_plain_ratio: { target: 0.20, tolerance: 0.20, weight: 0.08 }
- ko_register_polite_ratio: { target: 0.65, tolerance: 0.22, weight: 0.08 }
comma_per_sentence: { target: 0.6, tolerance: 0.4, weight: 0.08 }
suffix_class_diversity: { target: 0.40, tolerance: 0.16, weight: 0.08 }
over_edit_churn: { max: 0.40, weight: 0.06 }
diff --git a/personas/zh/blog-essay.md b/personas/zh/blog-essay.md
index c7c7d35..d989f0d 100644
--- a/personas/zh/blog-essay.md
+++ b/personas/zh/blog-essay.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: blog-essay
name: 个人博客随笔(中文)
lang: zh
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -44,7 +33,6 @@ blocks:
- 温度
- 纹理
- 脚步
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 1
explanation_habits:
active: true
@@ -58,7 +46,6 @@ blocks:
- 缺乏依据的笼统概括
sentence_structure:
active: true
- register: mixed
sentence_length_cv_target: [0.55, 0.95]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.6
diff --git a/personas/zh/natural-zh.md b/personas/zh/natural-zh.md
index b143d83..eb364b7 100644
--- a/personas/zh/natural-zh.md
+++ b/personas/zh/natural-zh.md
@@ -1,20 +1,9 @@
---
-schema: patina.persona.v1
+schema: patina.persona.v2
id: natural-zh
name: 朴素中文(去除 AI 腔)
lang: zh
source: library
-depth: content
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
blocks:
preferred_words:
active: true
@@ -50,7 +39,6 @@ blocks:
preferred_metaphors:
active: true
allow: []
- forbid_new_facts: true
max_new_metaphors_per_500_chars: 0
explanation_habits:
active: true
@@ -67,7 +55,6 @@ blocks:
- 号召口吻堆砌(务必、一定要、赶快)
sentence_structure:
active: true
- register: plain
sentence_length_cv_target: [0.5, 0.9]
paragraph_sentence_count_target: [2, 5]
opener_diversity_min: 0.5
diff --git a/personas/zh/preserve.md b/personas/zh/preserve.md
deleted file mode 100644
index 1b44b48..0000000
--- a/personas/zh/preserve.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-schema: patina.persona.v1
-id: preserve
-name: 保留原意默认
-lang: zh
-source: library
-depth: style-only
-persona_depth_directive:
- content_scope: emphasis-and-coverage-only
- mps_advisory: false
- fidelity_advisory: false
-mps:
- enforce: true
- floor: 70
-fidelity:
- enforce: true
- floor: 70
-blocks:
- preferred_words:
- active: false
- allow: []
- avoid: []
- density:
- target_per_1000_tokens: 0
- max_per_paragraph: 0
- preferred_metaphors:
- active: false
- allow: []
- forbid_new_facts: true
- max_new_metaphors_per_500_chars: 0
- explanation_habits:
- active: false
- moves: []
- avoid: []
- sentence_structure:
- active: false
- worldview:
- active: false
-target_features: {}
----
-
-# 保留原意默认
-
-此正文仅供文档说明,描述保留原意的默认人格,不会包含在执行提示中。
diff --git a/playground/README.md b/playground/README.md
index a59eb6e..9d7bc86 100644
--- a/playground/README.md
+++ b/playground/README.md
@@ -22,7 +22,7 @@ server-side and streams a humanized rewrite back. Invariants (pinned by
`tests/unit/web-deploy-invariants.test.js`):
- **Runtime**: Vercel Node Function at `/api/rewrite`. The function bundle MUST
- include `patterns/**`, `profiles/**`, `core/**`, `lexicon/**`, and
+ include `patterns/**`, `document-types/**`, `personas/**`, `core/**`, `lexicon/**`, and
`.patina.default.yaml` (`functions["api/rewrite.js"].includeFiles` in
`vercel.json`) because the patina loader reads them from the filesystem.
- **No-store / no-persistence**: the server never logs or persists request text,
diff --git a/playground/chatgpt.js b/playground/chatgpt.js
index 62edd79..6649938 100644
--- a/playground/chatgpt.js
+++ b/playground/chatgpt.js
@@ -7,9 +7,10 @@ import { createRewriteThread, streamRewrite, classifyRewriteError, REWRITE_ERROR
import launchConfig from '/launch-config.js';
import {
PROVIDER_PRESETS,
+ WEB_DOCUMENT_TYPES,
WEB_PERSONAS,
- TIER_LIMITS,
WEB_TIERS,
+ TIER_LIMITS,
MPS_FLOOR,
FIDELITY_FLOOR,
} from '../src/web-rewrite-contract.js';
@@ -24,7 +25,9 @@ const els = {
// nav controls (shared by landing + chat)
lang: /** @type {HTMLSelectElement} */ ($('#lang')),
tier: /** @type {HTMLSelectElement} */ ($('#tier')),
+ documentType: /** @type {HTMLSelectElement} */ ($('#document-type')),
persona: /** @type {HTMLSelectElement} */ ($('#persona')),
+ register: /** @type {HTMLSelectElement} */ ($('#register')),
provider: /** @type {HTMLSelectElement} */ ($('#provider')),
model: /** @type {HTMLSelectElement} */ ($('#model')),
apiKey: /** @type {HTMLInputElement} */ ($('#api-key')),
@@ -77,7 +80,7 @@ const I18N = {
ctaTitle: 'Paste your own and see',
ctaSub: 'Drop an AI-sounding draft into the box above. No code, no key.',
ctaBtn: 'Start at the top ↑',
- note: ['Deterministic humanizer —', 'same claim, numbers, tone.'],
+ note: ['Deterministic humanizer —', 'same claim, numbers, voice.'],
hint: 'patina changes only the wording — never the claim, numbers, or causation. Rewrites run the real patina pipeline server-side; MPS/fidelity are scored live.',
chatPh: 'Keep refining… (Enter to send · Shift+Enter for newline)',
newchat: 'New chat',
@@ -242,21 +245,21 @@ const PRO_I18N = {
const SAMPLES = {
ko: [
{ t: 'Marketing copy', x: '본 솔루션은 혁신적인 시너지를 활용하여 고객에게 전례 없는 가치를 원활하게 제공합니다.' },
- { t: 'Report tone', x: '결론적으로, 이러한 다각적인 접근 방식은 조직의 역량을 한층 더 제고하는 데 기여할 것으로 사료됩니다.' },
+ { t: 'Report register', x: '결론적으로, 이러한 다각적인 접근 방식은 조직의 역량을 한층 더 제고하는 데 기여할 것으로 사료됩니다.' },
{ t: 'Announcement', x: '저희는 여러분께 혁신적인 신규 플랫폼을 선보이게 되어 진심으로 기쁘게 생각합니다.' },
],
en: [
{ t: 'Marketing copy', x: 'Our cutting-edge, best-in-class solution leverages synergies to seamlessly deliver world-class value at scale.' },
{ t: 'Announcement', x: 'We are thrilled to announce that our innovative platform will revolutionize the way you work.' },
- { t: 'Report tone', x: 'In conclusion, this multifaceted approach will further enhance the overall capabilities of the organization.' },
+ { t: 'Report register', x: 'In conclusion, this multifaceted approach will further enhance the overall capabilities of the organization.' },
],
zh: [
{ t: 'Marketing copy', x: '本解决方案充分利用前沿协同效应,无缝赋能客户,释放前所未有的价值。' },
- { t: 'Formal tone', x: '综上所述,这种多元化的方法将进一步全面提升组织的核心竞争力。' },
+ { t: 'Formal register', x: '综上所述,这种多元化的方法将进一步全面提升组织的核心竞争力。' },
],
ja: [
{ t: 'Marketing copy', x: '本ソリューションは革新的なシナジーを活用し、お客様にかつてない価値をシームレスに提供します。' },
- { t: 'Report tone', x: '結論として、この多角的なアプローチは組織の能力を一層向上させることに寄与すると考えられます。' },
+ { t: 'Report register', x: '結論として、この多角的なアプローチは組織の能力を一層向上させることに寄与すると考えられます。' },
],
};
@@ -426,12 +429,26 @@ function syncTier() {
els.licenseSignOut.hidden = !signedIn;
}
-// Populate the Voice selector from the contract's per-language persona list.
-// The empty option = the server's default voice (ko preserve; en/zh/ja voice-free).
+// Populate opt-in voices. The empty option preserves the source voice.
+function populateDocumentTypes() {
+ const prev = els.documentType.value;
+ els.documentType.innerHTML = '';
+ for (const id of WEB_DOCUMENT_TYPES) {
+ if (id === 'namuwiki' && els.lang.value !== 'ko') continue;
+ const label = id === 'default'
+ ? 'Default'
+ : id.split('-').map((word) => word[0].toUpperCase() + word.slice(1)).join(' ');
+ els.documentType.appendChild(new Option(label, id));
+ }
+ els.documentType.value = Array.from(els.documentType.options).some((option) => option.value === prev)
+ ? prev
+ : 'default';
+}
+
function populatePersonas() {
const prev = els.persona.value;
els.persona.innerHTML = '';
- els.persona.appendChild(new Option('Default voice', ''));
+ els.persona.appendChild(new Option('Preserve source', ''));
for (const p of (WEB_PERSONAS[els.lang.value] || [])) {
els.persona.appendChild(new Option(p.label, p.id));
}
@@ -962,6 +979,8 @@ async function submit(text, source = 'hero') {
provider: els.provider.value, model: els.model.value,
apiKey: tier === WEB_TIERS.BYOK ? els.apiKey.value : undefined,
persona: els.persona.value || undefined,
+ documentType: els.documentType.value,
+ register: els.register.value || undefined,
});
await runAttempt({
convo, clean, reqBody, body, textEl, statusEl,
@@ -1211,6 +1230,7 @@ function onLangChange() {
applyI18n(els.lang.value);
renderSuggest();
populatePersonas();
+ populateDocumentTypes();
// Re-localize stateful button labels (e.g. an active Stop control's aria-label).
updateHeroSend(); updateChatSend();
const convo = activeConvo();
diff --git a/playground/index.html b/playground/index.html
index c3aa385..0594908 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -37,9 +37,21 @@
+
+