Voice ForgeTHE DOCUMENTATION
v2.9.1
SOURCE REFERENCE Generated from the current pluginskills/voice-forge/references/profile-format.md

Voice home file formats

The voice home is a private folder, ideally a private git repo linked to a private GitHub repo. Git keeps the history, so there is no version field in any file. To undo a change, revert the commit. vf.py setup creates it, vf.py sync and vf.py save keep it in sync; see setup.md.

Its location is saved per computer in ~/.config/voice-forge/config.json:

{ "home": "/Users/me/.voice-forge", "verified_private": ["https://github.com/me/voice-forge-private"] }

verified_private lists remotes confirmed private on this computer. vf.py pushes only to those, or to a GitHub repo it can check is private.

<voice home>/
  base.md                   the few rules true in every profile
  profiles/<name>/
    rules.md                the brief and the approved rules, in the ten section schema
    rules.migrated.md       only during a migration from the old layout; see migrate.md
    rules.update.md         only while bringing in changes from the profile it was derived from; see derive.md
    lessons.md              candidate rules waiting for evidence or approval
    topics.md               topics for training rounds
    rounds/                 one file per training or learning round
    analyses/               one file per analysis of past texts
    samples/                short examples the user confirmed as their voice
  holdout/                  texts for tests only; never read them
  evals/                    test results, blind comparisons and fidelity-log.md
  feedback.md               problems with the skill, one dated line each, under "## Log"
  voice-forge.json          settings shared by all computers, for example {"auto_push": true}

Profile names are lowercase with a language suffix: economics-en, explainer-es. default-en and default-es are the fallback profiles for everyday text.

Profiles are independent. An agent loads base.md and one profile, never two profiles at once.

Keep the formats below exactly. scripts/vf.py parses them.

rules.md

rules.md is the profile: the brief and every approved rule. It is the one source of truth for writers and reviewers. Evidence lives elsewhere (rounds, analyses, lessons); rules only cite it by ID.

A nonfiction profile follows the schema in schema-nonfiction.md: the ten sections below, in this order, with these exact headings. vf.py validate --profile <name> checks it. A file that breaks the format fails validation, even if its rules read well.

---
profile: economics-en
type: nonfiction              # nonfiction, fiction or unclassified
schema: nf-1
language: en
register: academic
audience: economists, referees and editors
---

# economics-en

## 1. Identity and context

- Language and register: English, academic
- Genre and document types: journal articles and working papers
- Purpose: report and defend empirical findings in development economics
- Readers: economists, referees and editors
- Field and venues: development economics journals
- Conventions: author year citations; methods in passive voice are fine
- Topics: poverty, inequality, household surveys
- Scope: research papers and reports; not emails or blog posts

## 2. Document architecture

Status: established

- R03 State the main finding in the first paragraph of the introduction.
  - strength: preferred
  - kind: constraint
  - applies: introductions of papers and reports
  - except: none
  - check: Read the first paragraph of the introduction. PASS if it states the main finding with its direction.
  - evidence: 20261005-1142, 20261007-0930, 20261009-1015
  - status: approved 2026-10-09

## 3. Argumentation and reasoning

Status: unknown

## 4. Paragraphs

Status: unknown

## 5. Discourse and coherence

Status: unknown

## 6. Sentences and rhythm

Status: established

- R01 Keep most sentences short; use a long one only to hold a list or a chain of steps.
  - strength: preferred
  - kind: tendency
  - applies: all prose
  - except: quoted text
  - check: Look at the text as a whole. PASS if long sentences are rare and each one holds a list or a chain of steps.
  - auto: share-sentences-over 30 15
  - evidence: 20261005-01, 20261007-02, 20261009-01
  - status: approved 2026-10-09
  - from: L003

## 7. Vocabulary and register

Status: unknown

## 8. Voice and metadiscourse

Status: unknown

## 9. Evidence and sources

Status: unknown

## 10. Anti-patterns and constraints

Status: established

- N01 Never use em dashes or en dashes as punctuation.
  - strength: prohibited
  - kind: constraint
  - applies: all prose
  - except: number ranges such as 1990–2000
  - check: PASS if no dash is used as punctuation.
  - auto: no-dashes
  - evidence: stated 2026-10-03
  - status: approved 2026-10-03

## Notes

Free text the user wrote. Not rules.

Format rules, all checked by vf.py validate:

  • Front matter has profile (the folder name), type, schema: nf-1 and language.
  • Headings are exactly the ten above, in order, then optionally ## Notes. No other ## headings.
  • Section 1 has one line per field: Language and register, Genre and document types, Purpose, Readers, Field and venues, Conventions, Scope. Topics is optional. An empty field is a warning, not an error. It has no status line and no rules.
  • Sections 2 to 10 start with Status: established, Status: unknown or Status: not_applicable. not_applicable needs a Reason: line under it. established needs one or more rules; the others need none.
  • Rules have every field in the rule contract: strength, kind, applies, except, check, evidence, status. auto and from are optional. Allowed values and meanings are in schema-nonfiction.md.
  • IDs: R01 for profile rules, N01 for "never" rules, which must be strength: prohibited. Each ID appears once. Never reuse an ID, even after a rule is dropped.
  • No TODO in any rule field. vf.py migrate leaves TODO markers for the user to fill.
  • No adapt field. vf.py derive adds one to each copied rule; delete it once the rule is adapted and approved.
  • Optional fields: auto, from and transfer (any, adapt, en-only, es-only).
  • Retired IDs: vf.py rule drop removes a rule and adds - Retired R07 (date): reason. Was: ... to Notes. vf.py next-id never hands out an ID that appears anywhere in the profile, its rounds or its lessons.
  • Each rule is one checkable action. "Be clear" is not a rule. "Put the claim in the first sentence of the paragraph" is.
  • evidence lists round IDs, analysis:<id>, stated YYYY-MM-DD when the user stated the rule, or adopted YYYY-MM-DD (analysis:<id>) for a suggestion the user adopted.

Derived profiles add two front matter fields: derived_from: explainer-es@3f2a9c1 (the source and its version) and stage: provisional. A copied rule's evidence reads transferred from explainer-es R07@1a2b3c4d, where the last part is a fingerprint of the source rule, so vf.py derive --update can see later changes. Not copied rules are listed in Notes as - not transferred: explainer-es R04 (es-only, ...): .... See derive.md.

Profiles with type: fiction or type: unclassified keep any layout; validation checks only their rule fields. A file in the older layout (## Brief, ## Rules, ## Never) fails validation with a hint to run vf.py migrate.

vf.py export --profile <name> prints the profile as JSON for scripts. The JSON is always generated from rules.md; never edit or keep it as a second source.

base.md

base.md holds the few rules true in every profile. It uses the same rule contract and the same section headings, but only the sections that hold rules, and no status lines or section 1.

---
profile: base
type: base
schema: nf-1
language: any
---

# Base voice

## 10. Anti-patterns and constraints

- B01 Never use em dashes or en dashes as punctuation.
  - strength: prohibited
  - kind: constraint
  - applies: all prose
  - except: number ranges
  - check: PASS if no dash is used as punctuation.
  - auto: no-dashes
  - evidence: stated 2026-10-03
  - status: approved 2026-10-03

- B02 Use «» for every first level quotation.
  - strength: mandatory
  - kind: constraint
  - applies: all prose
  - except: quotes inside a quote, which use “”
  - check: PASS if every first level quotation uses «».
  - auto: angle-quotes
  - transfer: es-only
  - evidence: stated 2026-10-03
  - status: approved 2026-10-03

## Notes

transfer: es-only makes B02 apply to Spanish profiles only. IDs in base.md are B01 (or N01 in files made before version 2.7). In review reports, base rules are written base:B01 so they never clash with profile IDs.

lessons.md

# Lessons: economics-en

## L003 Put the claim first, then the data
- status: candidate
- category: paragraphs
- rule: Open the paragraph with the claim. Give the number after it.
- strength: preferred
- kind: tendency
- applies: analytical body paragraphs
- except: paragraphs that describe data or methods
- check: In each applicable paragraph, PASS if the claim comes before the first number.
- evidence: 20261005-01 (poverty-lines), 20261007-02 (ppp-revisions), analysis:20261008-ppp-paper
- against:
- examples:
  - 20261005-01: "Over the past decades, poverty..." → "Extreme poverty fell by half..."
  • status is one of candidate, promoted, rejected.
  • evidence holds unique round IDs, each with its topic slug, and analysis:<id> entries from Analyze mode. Never list the same round twice.
  • against lists training rounds where the user did the opposite. Counterexamples found by Analyze mode stay in the analysis record (and may shape except); they never go in against.
  • category is one of the ten section slugs in schema-nonfiction.md: identity, architecture, argument, paragraphs, discourse, sentences, vocabulary, voice, evidence, constraints. Lessons written before version 2.7 may use older names (tone, rhythm, openings...); vf.py maps them, and migration asks about unclear ones.
  • Optional fields strength, kind, applies, except, check and auto hold the draft rule contract. At promotion they are copied into rules.md after the user approves the whole block.
  • A lesson is ready to promote when its rounds cover 2 or more topics and it has either 3 or more rounds, or 2 rounds plus an analysis. Analysis alone never promotes a lesson, because old or coauthored texts may not reflect the user's voice today. python3 scripts/vf.py status checks this. It counts analysis IDs, not the texts behind them: one analysis is enough, and more analyses never replace rounds or topics.

topics.md

# Topics: economics-en

- poverty-lines: Why the international poverty line moved from $2.15 to $3.00
- survey-weights: What survey weights do and when they mislead

One topic per line: a slug, a colon, a short description. The user can add or remove topics at any time.

rounds/

File name: <id>-<topic-slug>.md. The ID is the date and time, so two computers never pick the same one: 20261009-1142 (or 20261009-114233 if that minute is taken). Older rounds may use a two digit counter, 20261005-01; both count. Get the next ID with python3 scripts/vf.py next-round --profile <name>.

---
id: 20261009-1142
profile: economics-en
mode: train                   # train or learn
topic: poverty-lines
focus: mixed                  # mixed, openings, closings, transitions, data, hard or own
language: en
source: paper.md@3f2a9c1e0b7d..8d41aa07c2e9   # learn rounds from git only; printed by vf.py pair
---

## Passage

The text the agent wrote (train) or the AI draft (learn).

## Rewrite

The user's version, exactly as given.

## Findings

- NEW [paragraphs] ...
- CONFIRMED R01 ...
- CONTRADICTION R04 ...

## Decisions

- L003 yes (new)
- L001 yes (added evidence)
- "Avoid passive voice" no

samples/

Finished texts the user wrote or fully approved, one per file: <slug>.md.

---
language: es
register: explanatory
topic: sesgo-de-confirmacion
source: blog post, 2025
---

Text.

Rewrites in rounds/ count as samples too. Do not copy them into samples/.

analyses/

File name: <YYYYMMDD>-<slug>.md, where the slug names the main text. This ID is what lessons cite as analysis:<YYYYMMDD>-<slug>.

The record holds the full analysis; the conversation only shows a summary. It is written for people and agents: no script parses it, and vf.py validate checks profiles, not analysis records. vf.py holdout check reads it as text, and vf.py next-id notices rule IDs mentioned in it. Item IDs use F, P, Q and S, never R, N, B or L. Sections, in this order:

---
id: 20261016-stats-book
profile: stats-explainer-en
---

## Sources

- ch1-averages.md and ch2-surveys.md: chapters 1 and 2 of one book project; dates unknown; written alone; still likes them: yes
- magazine-2019.md: 2019, written alone, the magazine required endnotes; still likes it: mixed
- column-2024.md: 2024, written alone; still likes it: yes
- Reanalysis: none. (Write here when a source was analyzed before, or two files are versions of one text.)

## Coverage

- ch1-averages.md: read in full, including Table 1 and its caption.
- magazine-2019.md: read in full; the pull quote image could not be read.

## Measurements

Output of `measure.py stats`, per source text: sentence and paragraph lengths, lexical diversity, stance markers.

## Findings

- F01 [architecture] stable: gives an everyday analogy before the technical term
  - where: ch1 §2 "Think of a queue at the bank..."; magazine-2019 ¶3; column-2024 ¶2
  - function: the reader has a picture before the label
  - variation: longer analogies in the book, one sentence in the column
  - conditions: the first time a technical term appears
  - exceptions: column-2024 glossary box gives definitions first (a list of terms, no explanation)
  - alternatives: not a venue requirement; the magazine's other authors define first
  - scope: time (2019 and 2024; the book is undated)
  - profile: refines R02 (adds the order: analogy, then term)
- F02 [paragraphs] unsupported: three rhetorical questions in a row
  - where: ch2 ¶4, ¶9, ¶15; nowhere else
  - scope: text
  - profile: new; local, so a question (Q01), not a lesson

## Across sources

What each source adds, confirms or contradicts, written once across sources.

## Confirmations

- Confirms R01 in all four sources (opens with a concrete scene).

## Inconsistent passages

- P01 magazine-2019, ¶7, "It may perhaps be argued...": one long, heavily hedged sentence; nothing in its job explains the change.

## Decisions

- P01: someone else shaped it (the editor), 2026-10-16
- F01: keep → L012, wording approved 2026-10-16
- F04: dropped
- Q01: answered: "only in that chapter, on purpose"
- S01: no

## Pending

- F03: unsure
- F05, F06, S02: not shown yet
  • Findings use the section slugs and the source labels: stable (across separate works), stated (the user said it), genre (an external requirement; goes to the brief), coauthor (only in texts others shaped), old-habit (only in older texts), unsupported (too little evidence, including local patterns; never a lesson), suggestion (a field convention the user does not follow yet).
  • Scope is one of passage, text, project, works, time.
  • Profile relation is confirms, refines, tension, new or covered by Rxx.
  • Decisions record what the answer means: a fact, recognized (with the user's scope), keep → L012 only after the user approved the exact wording, dropped, pending, deferred, adopted as R07. Pending items stay listed until decided; nothing disappears.
  • Records written before version 2.9 have only Sources, Measurements, Findings, Inconsistent paragraphs and Decisions. They stay valid as they are.

← Back to the reference library

Find your next step