DR-0019 — .vendkit/ has two halves: consumer/ and publisher/
- Status: accepted
- Date: 2026-07-30
- Supersedes: refines DR-0012 (the
per-slice two-file rule holds; the directory gains a
consumer/level)
Context
.vendkit/ held the consumer control plane — slice configs and vendored
manifests — discovered by two non-recursive globs: .vendkit/*.yml and
.vendkit/*-manifest.json. The first is strict by design: every YAML file there
must parse as a slice config, and a stray one is a usage error rather than a
silent skip (DR-0012).
That strictness made the directory unusable for anything else, including a
publisher's own configuration. Publishers therefore kept their export
declaration, generated manifest, conformance rule set, and subscribers file at
the repository root — five machine-read files in the most-read directory in the
repo, and no way to group them without inventing a second convention per
publisher. A repository that both publishes and consumes had no way to express
that; its own manifest, dropped into .vendkit/, would have been discovered as a
vendored slice manifest and compared against its own tree.
A downstream publisher hit this directly: it had grouped its machine-read configuration into a directory of its own and found three of the five files were engine contract, not its choice.
Decision
.vendkit/ has two halves, and no glob reaches across them:
| Half | Holds | Owner |
|---|---|---|
.vendkit/consumer/ |
<slice>.yml, <slice>-manifest.json |
consumer (config) / engine (manifest) |
.vendkit/publisher/ |
export-declaration.yml, <slice>-manifest.json, conformance-rules.yml, subscribers.yml |
publisher |
The publisher-side paths become defaults, not merely permitted values:
--export-decl defaults to .vendkit/publisher/export-declaration.yml,
publisher.manifest_dir defaults to .vendkit/publisher (an explicit "." opts
back out to the repo root), and conformance --rules resolves
<publisher-root>/.vendkit/publisher/conformance-rules.yml when the flag is
absent.
Two supporting changes ride along:
slice.aliases— a publisher's self-declared names, copied into each consumer's slice config at onboard and sync time. A consumer's set of known upstreams is then the union of what its upstreams say about themselves, so tooling that reasons about cross-repository references never needs a hand-maintained registry of the whole family, and no repository declares another's identity.- A
rules=core-only|core+publisherfact on everyconformancerun.--ruleshaving no default meant a consumer could evaluate only the embedded core rules and read the clean result as "the publisher's slice rules passed". An explicit--rulesthat cannot be read remains a hard error; a missing conventional file is skipped, but never silently.
Consequences
- Positive: publisher and consumer roles are legible in one root, and a repository can hold both without collision. Publishers get a home for their machine-read configuration without inventing a convention. The defaults remove four flags from a typical publisher's call sites. Conformance can no longer silently evaluate half its rule set.
- Negative: MAJOR, with a migration every existing consumer must apply. The
publisher manifest moves out of the repo root, so any path filter, CODEOWNERS
entry, or branch policy naming it directly needs updating — entries scoped to
.vendkit/as a whole still cover both halves. - Neutral / operational: ships as v2.0.0 with migration
2026-07-vendkit-dir-split, whose obligationsmigrations-verifyenforces. VendKit's own repository moves with the change (self-hosting).
Alternatives considered
Keep the publisher's files at the repo root
The status quo. Rejected: it forces every publisher to accumulate machine-read files in its most-read directory, and offers no answer at all to a repository that both publishes and consumes.
Let publishers choose any directory, with no default
Shipped in v1.1.0 as publisher.manifest_dir plus explicit --export-decl and
--rules. Rejected as the end state: it works, but every publisher invents its
own layout, every call site must pass flags, and a forgotten flag is silent —
which is exactly how the conformance rule set came to be skipped in practice.
One flat .vendkit/ with filename conventions
E.g. publisher-*.yml beside <slice>.yml. Rejected: the strict-namespace rule
over .vendkit/*.yml would have to weaken to a prefix test, trading a hard error
for a heuristic on the one surface where a silent skip is most expensive.