PROP-051: vibe refactor — the source-refactoring umbrella
1. Mandate
01The owner's mandate, verbatim (2026-08-23,
chat, three clauses in one sitting): «я хочу все наши спецификации в
проекте (всё внутри spec и packages, итп) сконвертировать в XML как язык
исходников. … Это гораздо более продвинутый формат для нас, потому что
нейросеть понимает сложные спецификации в нем проще.» — «у тебя уже
где-то должна быть система бесшовного переписывания форматов
спецификаций. Я предлагаю сделать команду "vibe refactor" которая будет
зонтиком для разных команд рефакторинга. Дальше сделать команду vibe
refactor convert-source для конвертирования форматов исходников между
Markdown и XML. Если в ходе конвертирования будут происходить какие-то
деструктивные операции - выдается ошибка с описанием что потеряется и
потребуется подтверждение (либо интерактивное, либо флаг --force снимает
проверку). Проверку стоит делать честно, обратным переконвертированием.»
— «и дальше уже ты просто применишь этот инструмент к своим же файлам.»
The seamless-rewrite system the owner names already exists: it is
PROP-045's pivot (vibe-specdoc, parse → IR → emit, all four edges);
this PROP adds the user-facing verb over AUTHORED sources and the honesty
contract that verb must carry.
02The owner's fourth clause, verbatim
(2026-08-23): «файлы внутри vibedeps нигде не нужно переконвертировать
вручную, нужно вместо этого сконвертировать исходники и выполнить
материализацию». The law: convert-source operates on AUTHORED sources
only; materialised state (vibedeps/, the machine cache, generated boot
artifacts) is never converted in place — sources convert, then
vibe install re-materialises the slots under the standing PROP-045
machinery (the derived-manifest law re-transforms them honestly).
2. The umbrella
03vibe refactor <verb> is the command family for
meaning-preserving rewrites of authored source trees. Charter: a
refactoring changes the FORM of sources, never their meaning, and every
verb under the umbrella must carry its own machine-checked honesty proof
(for convert-source: the reverse-reconversion check of §3). First
citizen: convert-source; future verbs (anchor renames with tombstones,
renumbering) join under the same charter, each with its own PROP-level
fact before it ships.
3. convert-source
04vibe refactor convert-source --to
xml|markdown [PATH…] converts spec sources between the two PROP-045
serialisations (md accepted as an alias of markdown). Each PATH is a
file or a directory; a directory walks recursively and selects spec
sources (is_spec_source) currently in the OPPOSITE form; a file in the
target form already is reported already and skipped. The walk always
skips: vibedeps/ (##VIBEDEPS-NEVER-CONVERTED), dot-directories,
target/, and any file whose leading lines carry the generated by
vibe marker (generated artifacts regenerate; they are not authored
sources) — an EXPLICIT file argument overrides only the marker skip,
never the vibedeps/ skip.
05A conversion writes the sibling
serialisation and deletes the original in the same act, so the
one-document-one-form law (PROP-045 ##TARGET-MIXED, the pair-collision
check) holds at every instant — the tree never holds X.md and X.xml
together, not even transiently between files. The verb assumes version
control underneath it and keeps no backups of its own.
06The destructiveness check is the owner's law,
implemented by reverse reconversion through the pivot: for a source
S, parse to IR, emit the target form, read the target form back, and
project it into the SOURCE form again; compare. Three classes:
(1) byte-stable — the back-projection equals S byte-for-byte:
converts silently. (2) IR-stable loss — the back-projection
re-parses to the SAME IR but differs in bytes (dropped MD/XML comments,
normalised layout): the verb REFUSES with a per-file description of
what is lost, and proceeds only on interactive confirmation or
--force. (3) IR-divergent — the back-projection re-parses to a
DIFFERENT IR: always refused, --force does not apply; that class is a
vibe-specdoc defect to file (the pivot broke its own round-trip law),
never a corpus to damage.
07The class-2 description is honest and concrete: it
names the file and shows the lost or changed lines (a unified diff of
the source against its back-projection), with dropped comments called
out as CONTENT loss (PROP-045 ##REVIEW-COMMENTS-LAW: the pivot drops
comments by design — REVIEW: markers and prose comments do not
survive conversion) rather than folded into formatting noise.
08On a TTY, class-2 files prompt per file
(yes / no / all); off a TTY, class-2 without --force is an error
listing every lossy file and what each loses. --force waives class 2
only. --dry-run classifies and reports every file, writes nothing,
and always exits 0 (an inventory, not an attempt); a real run exits 0
iff every requested conversion landed, non-zero when any file was
refused or declined.
09The classification core — (source text,
direction) → class + emitted target + loss diff — lives in
vibe-specdoc, the crate that owns the IR and all four edges (PROP-045
##PIVOT-MODEL), so the honesty check is a library property with its own
unit tests; the CLI verb is a thin walker / prompter / writer over it,
in the house thin-command genre.
10Two skip classes the K2 inventory proved
(2026-08-24), amending ##CONVERT-SOURCE-SURFACE: the walk also skips
node_modules/ directories — foreign vendored trees are the same
genre as vibedeps/, never authored here (the inventory found 129
third-party .md under package tool dirs, including files only the
foreign author may reshape) — and harness-contract basenames
(CLAUDE.md, AGENTS.md, GEMINI.md, MEMORY.md, SKILL.md,
README.md, LICENSE.md — the last two by the owner's 2026-08-24
ruling, verbatim: «это же зарезервированные имена, это не
спецификации… чтобы на сайтах типа Гитхаба они отображались
нормально»; the K3/K4 flip converted 100 READMEs and 72 LICENSEs
before the ruling and they were converted back the same sitting):
their FILENAME and format are an external harness's contract (a
SKILL.md carries YAML frontmatter the pivot does not model — the
inventory showed conversion would strip it and break the skill), so
they stay Markdown by law, reported as their own skip class rather
than silently converted. An explicit file argument does NOT override
either skip — unlike the generated-marker skip, these are not
heuristics.
11Both directions are first-class on every converting
verb (owner, 2026-08-24, verbatim: «В настройках у обеих должно быть из
какого формата в какой (я так понял что ты запланировал только MD->XML,
а хочется чтобы была и XML->MD. Параметры --from и --to)»). The surface
is --from <markdown|xml> + --to <markdown|xml> (md an alias of
markdown on both): --to is required, --from defaults to the
counterpart of --to, and --from equal to --to is a loud error —
never a silent no-op. --from is a FILTER: only sources currently in
that form are selected; everything else reports already/skips
unchanged.
12vibe refactor convert-package-src [--from …]
--to … <package-root>… converts WHOLE PACKAGES, not individual files
(owner, 2026-08-24, verbatim: «Она будет конвертировать целиком пакеты,
в отличие от предыдущей команды, которая конвертирует конкретные
классы»). Each argument is a package root — a directory carrying
vibe.toml (anything else is a loud error; that validation is what
distinguishes this verb from pointing convert-source at a bare
directory) — and the conversion walks the package's SPEC HOMES with
the same core, the same skips and the same honesty contract as
convert-source (##HONESTY-BY-REVERSE applies file by file).
The perimeter ruling (owner, 2026-08-24, verbatim: «нужно переводить
спецификации из директории спецификаций (spec, packages, всякие
зарезервированные имена в корне если они есть), а не все подряд
файлы» — «тут дело не столько в том, что README.md это
зарезервированное имя, сколько что ты конвертировал всё подряд»):
the spec homes are spec/ and a nested packages/ — and NOTHING
else: root-level working docs (one house's WAL/checkpoint family) are
NOT presumed, because the verb serves every project and such names may
mean anything elsewhere (owner correction, 2026-08-24, near-verbatim:
«не у всех проектов есть эти файлы… это конкретно особенность vibevm.
А утилита рефакторинга работает на все проекты»); a house converts
its own root extras with explicit convert-source paths. Everything
else at a package root — code, tooling, reserved-name docs — is
outside the verb's perimeter, with the reserved-name skips of
##FOREIGN-AND-HARNESS-SKIPS as the second, independent guard. A root
carrying neither spec home is a loud refusal.
13vibe refactor convert-spec-src [--from …]
--to … [<package-root>] converts the spec/ directory of the chosen
package; with no argument it targets the CURRENT ROOT PROJECT's
spec/ tree (owner, 2026-08-24, verbatim: «она конвертирует
директорию spec для выбранного пакета (если не сказано что -
конвертирует текущий корневой проект)»), resolved the same way the
other project-rooted verbs resolve it.
14The two package-shaped verbs are thin
wrappers: they resolve WHICH directories to convert and then run the
very same walker and classification core convert-source runs (owner,
2026-08-24, verbatim: «Внутри они используют тот же код что
convert-src, вызываемый для разных директорий по смыслу») — no second
conversion path, no second honesty check to drift. Canonical names
follow the owner's own spellings (convert-package-src,
convert-spec-src), and convert-src rides as an alias of
convert-source, so both spelling families the owner used in chat
resolve.
4. Applying it to our own tree
15The mandate's third clause is a build of its own:
the host spec/ tree and every packages/ member convert
Markdown → XML with this verb, vibedeps/ re-materialises (never
hand-converted), and the by-name references to .md spec paths in
prose and contracts (inline links, CLAUDE.md's boot pointers, hooks,
tooling docs) are swept to the new names — no-legacy, by the standing
F5 precedent (PROP-050 ##MIGRATION-NO-LEGACY genre: one form, no dual
reading of retired shapes). Slices, perimeter and the dry-run inventory
live in the application plan: vibevm/vibespecs/terraforms/
CONVERT-SOURCE-APPLICATION-v0.1.xml. CLAUDE.md / AGENTS.md /
GEMINI.md themselves stay Markdown — their names are harness
contracts, not spec sources.
5. History
162026-08-23: mandated, designed and ratified in
one owner sitting, immediately after the PROP-050 visibility landing;
authored against the already-BUILT PROP-045 pivot (recipe specdoc/4,
host lane already STATIC.xml, host spec_format = "xml"), which is
why the whole feature is one verb and one application wave rather than
a format migration.