VibeVM
Contents
On this page
en
Publisher
org.vibevm.core
Version
1.0.0latest
Audiences
Reading time
11 min
Rendered
Read aloud
never

PROP-047 — Progress Control: the campaign toolchain

1. Mandate — the process layer

01The owner's boundary ruling (2026-08-22, chat, near-verbatim): «факты являются чем-то, поверх чего можно построить совершенно разные процессы рефакторингов, и не факт, что наша текущая кампания и способ вообще делать кампании — самый лучший. Всё, что касается самого синтаксиса, IR, операций над фактами — в модуль facts (это будет использовать широкая общественность); в progress оставить наши инструменты для рефакторингов vibevm и потихоньку доделывать, чтобы когда-то они стали достаточно хороши, чтобы показать миру.» This document is that progress layer: the tool, its config, evidence providers, campaign data contracts, and the maintenance discipline. The grammar it operates on is PROP-043 (spec://org.vibevm.core/vibevm/modules/vibe-facts/PROP-043), and the dependency points strictly upward: progress knows facts; facts never knows progress.

02The needs this layer answers (moved verbatim from the founding motivation of PROP-043 §1):

  1. 03an algorithmic tool that reports the state of the whole observed tree and enforces exhaustiveness when a campaign demands it;
  2. a substrate for the actualization campaign: mark every claim, verify it against the code, and drive the drift down — with the markup remaining afterwards as the steering mechanism for further development.
  • 04The vibevm adapter contributes the vibe progress CLI surface (§3), the facts.toml discovery, and the specmap evidence provider (§4). All vibevm-specific knowledge lives here.

2. Scope configuration — moved to the facts home

05Scope configuration is a facts-layer concern and lives in PROP-043 §6 (owner correction 2026-08-22: «what is observed» is a universal question every facts consumer answers — the campaign toolchain merely consumes the same scope through the facts core). The config file is facts.toml; progress.toml is read as a silent legacy fallback for the transition. This heading keeps its number so the section map below stays stable.

3. The tool — vibe progress

  • 06A subcommand of vibe (adapter over the standalone core).
  • Native output is XML; --md renders the table form (source · stage · state · action · comment); --json emits the state projections of §5.2.
  • All subcommands are incremental over the content-hash cache (§5.1).
  • 07scan {#scan} — parse the observed tree, build/update the cache and state projections.
  • check {#check} — validation gate: closed vocabularies (with nearest-value hints), well-formedness (unclosed point markers), placement rules (standalone-between-paragraphs), shorthand collisions, foreign-grammar non-collision, lossless folds. --exhaustive additionally requires zero unmarked paragraphs in scope — the campaign gate. Exit codes are stable for CI.
  • report — the tree status: XML native, --md table, --json. Filters: --view done|todo|qa|remove|doc|terminal (the six resolution views: state=done · action=continue · stage=test&state=plan|work · action=remove · actionstage=doc · derived terminal artifact closure), --audience user|author|dev|agent (agent admitted 2026-09-11 by PROP-057 through PROP-043 ##AUDIENCE-VALUES; the doc view lists the documentation's obligations and feeds the coverage gate vibe doc check --coverage, PROP-057 §14), per-file and whole-project rollups, explicit-vs-computed columns, and the evidence column when a provider is wired (§4).
  • mirror — materialize the per-file cache view (campaign working representation; §5.1) under the campaign zone.
  • weave — algorithmic stitch of the observed corpus into one document for whole-context LLM loading. --digest emits the map form (headings + markers + unmarked counts — always fits); --max-tokens N shards the full form with a shard manifest. Measured 2026-07-26 on the 58-file wave-1 corpus: the full weave is one shard of 1 138 441 bytes (≈ a third of a 1M-token window, so the sharder never had to split) and --digest is 200 454 bytes.
  • rescan --baseline <file> {#rescan} — the recurrence entry point: three-way compare (sources ↔ markers ↔ baseline, §5.3) emitting new / changed(suspect) / carried-forward unit lists, plus "marker changed outside any campaign" flags.
  • baseline [--out <file>] {#baseline} — write the campaign's baseline.json (§5.3), the file rescan consumes. Projects the cache's fact-grain verdicts onto the unit granularity the baseline contract is defined at: a fact rolls up into every unit whose span carries it, the worst verdict wins (drift > unverifiable > confirmed), evidence is the deduplicated union, and the marker snapshot is resolved by the same code path rescan compares against. It re-verifies nothing and invents no verdict — a unit with no judged fact is omitted rather than filled in, so the artifact fails toward re-verifying. Default output is campaigns/<id>/baseline.json.
  • seal <path>… — record that a file's verdicts hold for its current text: sets content_hash and campaign.processed_hash to the digest recomputed from disk, plus verified_at. Same shape as ##CMD-GATE — the caller did the real re-derivation and this records it; the command computes, changes and invents no verdict. Reading the cached content_hash instead of the disk would defeat the purpose, since that field is refreshed only by scan and between scans compares one stale value with another. It refuses a file whose markers are not all judged (naming the count and the first few), refuses a path the cache does not carry, prints what it is vouching for before doing it, and is a no-op with no fresh timestamp when the digest already matches. Its refusal is a coverage test, not a recency one — the schema carries one date per file and none per verdict, so "every marker has a verdict" is checkable and "every verdict is fresh" is not; the operator asserting the seal is the real gate (F-075).
  • gate {#gate} — record one gate's verdict into the campaign's gate panel in campaign.json. The automation seam: whoever ran the real gate reports the result here, and the dashboard reads it back out. Spawns nothing and computes nothing — gates are recorded, never run here.
  • resume {#resume} — render RESUME.md from the campaign journal and state (operates on the campaign zone when present; a no-op outside one).

4. Evidence providers

  • 08The core defines a seam: given a unit, return external facts about it.
  • The vibevm adapter wires specmap (PROP-014) into it: implements / verifies / deviates edge counts per unit.
  • report then flags markup-vs-reality mismatches — e.g. a unit marked test/done with zero verifies edges. An unclassified freeze/done specmap orphan retains the legacy implementation warning; a classified frozen fact is checked only against the artifact kinds in its own @requires set. check can gate on the worst mismatch without inventing an undeclared implementation obligation.
  • A project without specmap runs with an empty evidence column; nothing in the core knows the provider's shape.

09Verification verdicts (confirmed / drift / unverifiable) are campaign data and live in the cache and baseline — never in the markup (§5.5).

10Fact-grain evidence (2026-07-24, owner-directed): the specmap side recognises @fact:<ID> fact anchors as addressable units (PROP-014 §2.1, the fact amendment's twin), so implements/verifies edges land per fact and the provider's mismatch checks apply at the campaign grain, not only per section.

11The evidence seam also serves PROP-043 ##TERMINAL-PER-ARTIFACT: for each declared terminal artifact it reports satisfied | missing | unavailable and exact locators when it has them. The facts core itself satisfies self-carried specification/decision/research/plan/disposition artifacts from their explicit declaration on the addressed state=done fact; the vibevm specmap adapter observes exact implements, verifies and documents relations; an external artifact stays unavailable until an observer can check its ref. A provider absence and a known zero are never conflated.

12report renders the authored canonical requires set, each artifact observation, and the derived unclassified | pending | terminal result. A separate terminal view selects only terminal facts; the existing done view remains exactly state=done. XML/Markdown/JSON projections share one row model. Dashboard/state projections consume that result and never recompute it from partial cache fields.

13A terminal observation answers only whether the fact's declared artifact set is currently satisfied by this provider snapshot. Campaign verdicts (confirmed | drift | unverifiable), lifecycle verification evidence and adoption remain separate axes. The observation is never written into authored markup, the tracked cache or a baseline as truth.

5. Data contracts

14All formats are schema-versioned ("schema": 1); all writes are atomic (tmp + rename); the journal is append-only JSONL (a torn tail line is discarded on read).

5.1 Cache (per-file records)

15Per observed file: path, content-hash, extracted markers with positions, unit/paragraph counts, unmarked count, rollup results; campaign fields when a campaign is active: verdict per marker (confirmed / drift / unverifiable), evidence refs, batch id, processed hash.

16The per-file verdict tally is computed on read, never stored beside the verdict map it counts (F-077, owner ruling 2026-07-26). A stored tally is a second statement of the same fact with its own writer, and this campaign measured three that had gone stale — including one that claimed a drift row already closed. The map is the source; the count is a view of it.

5.2 State projections (dashboard food)

  • 17campaign.json (wave, stage-of-campaign, gates, counters, updated_at), corpus.json (per-file rollups and counts), findings.json (the stitching obligation ledger), tasks.json (both task corpora with statuses), docdebt.json (harvest cards, doc-coverage).
  • The dashboard reads only these; it computes nothing and parses no Markdown ever.

5.3 Baseline (inter-campaign contract)

  • 18baseline.json — per unit: URI#anchor, unit content-hash at verdict time, verdict, evidence refs, date, named crates, marker snapshot. Shipped: baseline.rs's BaselineUnit carries exactly these fields, with Baseline::load, Baseline::store (baseline/project.rs), the ##CMD-BASELINE writer and the rescan CLI all live. store was claimed here before it existed and was built to match on 2026-07-26 (F-065); the round trip — write the baseline, rescan against it on an unchanged tree — is what pins the two halves together.
  • Invalidation: unit hash changed ⇒ suspect; named crate has commits after the verdict date ⇒ suspect; marker diverged from snapshot without a campaign ⇒ flagged; otherwise carry-forward (plus a small random control sample, because code-side invalidation is deliberately coarse).

5.4 The campaign zone

  • 19campaigns/<id>/ at the repository root: baseline.json, deferrals.md, harvest/, tasks/, and the ephemeral run/ (journal.jsonl, state/, RESUME.md, mirror/).
  • Excluded from markup scope, from packaging, and from registries — always.
  • run/ is disposable after close-out; the other four survive between campaigns.
  • Process law (journal step protocol, recovery rules, RESUME contract) lives in the campaign plan, not here.

5.5 The erasure law

  • 20Delete every derived artifact — cache, state, journal, mirror, weave — and no fact is lost: the markup in the sources carries all knowledge.
  • The one artifact worth keeping anyway is baseline.json: not knowledge but acceleration — its loss returns the next run's cost from O(delta) to O(corpus).

6. Maintenance discipline

21After the first campaign:

  • 22Edit a unit ⇒ update its marker in the same commit. vibe progress check sits in the gate panel and yellows on divergence.
  • Task pipelines close the loop: an IMPL task cites markers on entry and updates them on exit (impl/work → impl/done, then test/plan).
  • A classified freeze/done fact requires the artifact evidence its own @requires set names; freeze alone never manufactures an implementation obligation. An unclassified frozen fact retains the legacy implementation-edge warning during migration, so introducing terminal contracts cannot silently weaken an existing gate.
  • Doc-coverage (units lacking documents edges / doc-view closure) ratchets like specmap orphans.
  • Periodic re-verification runs as a recurring campaign (O(delta) via §5.3) and as a health-audit category between runs.

6.1 The life of a fact under an active campaign

23Editing the corpus while a campaign judges it is the normal case, not an exception — the campaign exists precisely because the corpus is being reworked. Three things can happen to a fact, they are not the same thing, and only one of them announces itself.

  • 24A judged fact whose text moves comes due for re-judgement, and the tooling names it. The freshness reader compares the text a fact was judged against with the text on disk and lists every fact that moved, by anchor. This is the case the machinery was built for.
  • A fact added to an already-judged file is unjudged, and NOTHING says so. It does not enter the verdict total, it does not appear in any percentage, and no gate fires. It is discovered only by comparing the file's addressable anchors against its verdict map — which no shipped command prints today.
  • A fact removed from a document leaves its verdict behind, and the verdict keeps counting. The cache is keyed by anchor and nothing prunes a key whose anchor is gone.
  • «The file moved» and «a judged fact moved» are different questions, and conflating them wastes the whole point. A file goes stale the moment its bytes change — including when the change only ADDS facts, leaving every judged fact untouched. A corpus can carry five stale files and zero facts owed re-judgement. Read the per-fact answer, never the per-file one.
  • Sealing refuses a file carrying any unjudged marker, and this is correct rather than inconvenient. Sealing asserts that every verdict in the file is valid for its current text, so a partially-judged file may be left flagged but not vouched for. That refusal is the only mechanism today that makes an added fact visible at all.

6.2 Incremental debt clearance

25The debt is enumerable, so it is paid item by item and never by re-judging the corpus. Three enumerable kinds: facts with no verdict, facts whose text moved, verdicts whose anchor is gone. Each has names and addresses; none is a percentage to be attacked wholesale. Re-judging everything would redo work that nothing invalidated.

26The unit of clearance is one file, because sealing is a whole-file assertion (##SEAL-IS-A-WHOLE-FILE-ASSERTION) — a file is either clear or flagged, and there is no half-sealed state to leave behind.

27The cheapest debt is in the file you were going to read anyway. Judging N facts in one document costs far less than N facts in N documents, because the reading is shared; a session already editing a document pays almost nothing to clear that document's backlog in the same pass.

28The procedure, run on demand and never automatically:

  1. 29Measure. Print the three kinds with the files behind them, worst first.
  2. Pick one file — either the heaviest, or the one this session is about to touch anyway.
  3. Judge only its unjudged facts, to the ordinary standard and clause by clause. A prescriptive fact is judged on coherence and on every referent resolving; a descriptive one is checked against the tree. Freshly authored text is not exempt from either.
  4. Merge and seal. A refusal to seal means something in the file was missed — that refusal is the check, not an obstacle.
  5. Report how much was cleared, so the number moves visibly rather than silently.

30Content moved into a specification is judged in the same pass that moves it. An unjudged statement in a spec is the same kind of tail as a dangling citation: the move is not finished until the corpus knows about what arrived. Without this the standing ruling «significant content moves into the specifications on closure» manufactures debt at every closure.

31A session reports the debt when it restores context (owner ruling 2026-08-06) — one line in the resume report, beside the gate state and the blockers. Reporting is not paying: the session says what the debt is and waits, because clearing it is work like any other and its priority is the owner's.

32The debt is a question the tool answers, not a query somebody reconstructs (owner ruling 2026-08-06). «How much debt is there for the periodic clearance» must be answerable by asking vibe progress, in the same breath as the confirmed/drift figures — three counts and the files behind them. A number that exists only in a hand-written query is a number nobody looks at, and this whole subsection describes work that is invisible until it is printed. The campaign-side script is a stopgap; the durable home is the shipped verb.

33What must not happen: clearing the count by judging without evidence. A verdict written to move a number is the defect this whole apparatus exists to remove, and it is cheapest to commit exactly when someone is paying down a backlog.

7. Out of scope / future

  • 34second-wave corpora (vibevm/vibepacks/org.vibevm.world, org.vibevm.ai-native, ~230–250 authored files) and the fractality specspace (explicitly excluded from wave 1 by owner decision);
  • dashboard evolution beyond the minimal read-only page.
  • (Terminology note: this surface is always called the dashboard — never "storefront", a term already taken by the vibevm store surface.)

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.core/vibevm@1.0.0/modules/vibe-progress/PROP-047-progress-campaigns

.md.xmlllms.txt