Configuration reference

Configuration lives in .vibey-gh.toml. Every key is optional; omitted values use the defaults below. Paths are repository-relative unless stated otherwise.

[fingerprint]

Field Type / default Meaning
text string / built-in provenance sentence Required source-header text.
trailer string / Made-With: ... Required commit trailer.
sources string list / Python and workflow globs Files checked for headers.

[version]

Field Type / default Meaning
files string list / empty Version-bearing files updated by version --apply.
content_paths string list / empty Paths whose changes produce a minor bump.
code_paths string list / ["src/"] Paths whose changes produce a patch bump.

[branches], [merge_train], and [install]

Field Type / default Meaning
branches.integration string / develop Integration and preview branch.
branches.release string / main Production branch; managed automation never deletes it.
merge_train.owner string / empty Normalized repository-owner login.
merge_train.trusted_authors string list / empty Authors exempt from outside-author review.
install.workflows string list / all managed workflows Exact managed subset; [] installs hooks and CLI assets only.
install.union_merge_paths string list / ["CHANGELOG.md"] Files declared merge=union in .gitattributes, so two branches appending to the same section merge instead of conflicting. Appended to an existing .gitattributes, never rewriting it. [] declares none.
install.pin_version boolean / false Pin every managed workflow's pip install vibey-gh to the exact version that rendered it (vibey-gh==X.Y.Z), instead of the latest release on every run. false keeps the historical floating install. The self-hosting path (this repository, and anything else installing from its own pyproject.toml) is never pinned — it installs from source regardless. Running vibey-gh install from a newer release moves the pin forward as one visible diff.

[ai]

Where every AI step sends its requests. Unset, nothing changes: requests go to Anthropic exactly as before this existed.

Field Type / default Meaning
base_url URL / empty Endpoint override. Empty uses the Anthropic default. Must be http(s) and contain no whitespace.
auth_secret secret name / ANTHROPIC_API_KEY The repository secret authorising those requests. A name only — never a token; this file is committed.

Running the automation somewhere other than Anthropic

Every AI step runs Claude Code, which honours ANTHROPIC_BASE_URL. Any gateway serving the Anthropic Messages API — LiteLLM and similar translate it to Gemini, Qwen, GitHub Models, a model on your own machine — therefore works without this project learning a second vendor's request shape:

[ai]
base_url = "https://your-gateway.example/v1"
auth_secret = "LITELLM_KEY"

That secret fills both x-api-key and Authorization, because Claude Code sends the former while some gateways read the latter. A gateway must serve /v1/messages and /v1/messages/count_tokens, and forward the anthropic-beta and anthropic-version headers.

One caveat worth testing before you rely on it: the review and repair steps depend on structured JSON output and tool calls, and translation layers vary in how faithfully they carry tool_use arguments across providers. A provider that mangles them makes the gate report review incomplete rather than approving anything — it fails closed — but the review is then no longer running. Verify against a real pull request before turning off the endpoint you trust.

auth_secret is validated as a bare secret identifier. It is rendered inside a ${{ secrets.… }} expression in a privileged workflow, so a name that could close that expression is refused at load time.

[pr_automation]

Field Type / default Meaning
enabled boolean / true Enable event-driven evaluation, review, repair, and gating.
scan_workflows string list / CI, Provenance, CodeQL, Docs, Conventional Commits Workflow names that trigger evaluation. Every name must be a workflow with a pull_request or pull_request_target trigger — one that only runs on push can never complete for a pull request, so state never leaves pending, the gate never publishes, and — made a required check — no pull request can ever merge. vibey-gh check fails on any named workflow that exists but cannot fire for a pull request; a name absent from .github/workflows/ is not an error.
ignored_checks string list / orchestration checks Checks excluded from the ordinary rollup. Own checks are always ignored.
max_repair_attempts integer / 3 (1–10) Repair budget per contributor lineage.
model string / claude-sonnet-5 Review and repair model.
review_untrusted_authors boolean / true Require exact-head outside-author review.
repair_untrusted_authors boolean / true Permit constrained outside-author repairs.
replace_fork_prs boolean / true Repair forks through linked repository-owned PRs.
retain_schedule_backstop boolean / true Retain scheduled recovery beside event triggers.

[pr_automation.observability]

Field Type / default Meaning
sanitized_progress boolean / true Request safe action progress only for Claude-supported direct PR/issue events; automated workflow events retain phase-level job visibility.
archive_execution_file boolean / true Retain each Claude execution record as a 90-day workflow artifact.
allow_private_full_output boolean / false Permit an explicit manual diagnostic run to emit raw Claude JSON, but only in a private repository.

Raw output additionally requires a manual workflow_dispatch with full_claude_output = true. Event-triggered runs can never enable it, and the workflow fails closed when repository visibility is not private.

[pr_automation.fallback]

Reviews with a local model when the paid path returns no verdict at all — an exhausted API key, expired credentials, an unavailable model. Because the gate is a required check, that failure otherwise turns a billing problem into a hard stop on every pull request.

Field Type / default Meaning
enabled boolean / false Off unless a repository opts in. It needs a self-hosted runner, so nothing should inherit it.
runner_label string / "vibey-local" Label the fallback job targets, alongside self-hosted.
model string / "qwen2.5-coder:14b" Model tag served by the Ollama-compatible endpoint.
base_url string / "http://127.0.0.1:11434" Where the local model listens.
trusted_only boolean / true Never run the fallback for a fork pull request.
max_diff_chars integer / 60000 Diff is truncated past this, and the model is told it was.
timeout_seconds integer / 600 Bound on one review.

It never overrides a review that actually ran: the job requires the primary to have produced no verdict, so findings are never discarded in favour of a weaker opinion. The diff is passed to the model as text — repository code is never executed, and the model has no shell, no tools, and no network beyond the local port.

Fetching that diff prefers gh pr diff, but GitHub's diff API refuses pull requests beyond roughly 300 changed files — exactly the shape of a large migration or adoption sweep, which would otherwise never be reviewable at all. When the API refuses, the job reconstructs the same merge-base diff locally instead: it fetches the base and head refs, deepening a shallow trusted checkout until their histories connect, and diffs one against the other. That reconstruction is read-only and executes no repository code, so the guarantee above holds either way, and max_diff_chars still caps what actually reaches the model.

The verdict is deliberately narrower than the primary review's. Ollama constrains decoding to the schema, so the output shape is guaranteed; the judgments are not, and a 14B model will emit confident booleans it has no basis for. So it assesses only what it can ground in a diff — pass, summary, findings — and reports the documentation-contract fields as unevaluated. The gate titles the result PR automation: gate (local fallback) so a degraded verdict is never mistaken for a full one.

trusted_only carries the safety argument. GitHub says self-hosted runners should "almost never be used for public repositories" because any user can open a pull request against them; excluding forks is what removes that. Leave it on, register the runner as ephemeral so it takes one job and exits, and run it in a container rather than on the host.

[conversation]

Answers a mention in a comment on an issue or pull request. Comments are the least guarded input a repository has, so the defaults are closed.

Field Type / default Meaning
enabled boolean / true Respond to mentions at all.
trigger string / @vibey-gh The mention that addresses the automation. Matched on a word boundary, so @vibey-gh-bot is not a mention.
model string / claude-sonnet-5 Model that reads the thread and answers.
max_interactions integer / 10 (1–100) Responses per thread, so a conversation cannot become an unbounded work queue.
respond_to_untrusted boolean / false Answer commenters outside the owner/trusted set. A response costs tokens, so answering everyone is a deliberate spending decision.
allow_changes boolean / true Permit file changes. Only ever on a pull request, only from a trusted commenter, and never on a fork or permanent branch.
ignore_actors string list / the automation's own bot identities The loop guard. Its own reply mentions the trigger too; answering it would run and bill forever. Cannot be empty while enabled.

An issue is answered in words only — there is nowhere to put a commit. A pull request from a trusted commenter may also receive one guarded commit on its own branch.

[branch_sync]

Keeps open branches current so conflicts never accumulate, and refills a spent repair budget a bounded number of times so a transient outage does not become a permanent stop.

Field Type / default Meaning
enabled boolean / true Run the sync and self-heal jobs at all.
update_contributor_branches boolean / true Merge the integration branch forward into branches this automation does not own, using GitHub's own update-branch endpoint. Never a rewrite.
max_self_heals integer / 2 (0–10) How many times one lineage's repair budget may be refilled before it stays exhausted for a human. 0 disables self-healing.

A fork is only ever moved forward, never rewritten: update-branch succeeds only where the contributor left "allow maintainer edits" enabled, so it carries their consent. Rebasing, closing, and deleting are unreachable for a fork under every setting.

[realign]

Realign converges the integration branch onto the release branch with a lease-protected force update, which replaces commits with rewritten copies and strands any topic branch cut from one of them. These keys decide how much the automation may do about that unaided.

Field Type / default Meaning
reconcile_branches boolean / true Reconcile open pull-request branches after a realign rewrite.
automation_prefixes string list / ["vibey-gh/"] Branch prefixes this automation may rebase on its own. Everything else is a human's to rebase.
close_duplicates boolean / true Close a pull request whose every commit is already upstream by patch identity.
delete_duplicate_branches boolean / true Delete that branch too. Permanent, fork, and unsafe refs are refused by name regardless.
notify_contributor_branches boolean / true Comment on a human's stranded branch with the rebase command instead of rewriting it.

Decisions use git cherry, which compares by patch identity, so a commit re-created upstream under a new SHA is correctly recognised as already present. A ref that cannot be read reports unique work rather than none, so an unreadable branch is never closed.

[issue_automation]

Turns a published issue into a reviewable pull request. Every field exists because an adopting repository could reasonably disagree with the default; the defaults themselves are closed, because anyone with a GitHub account can open an issue.

Field Type / default Meaning
enabled boolean / true Enable evaluation and autonomous solution proposals. false keeps the workflow installed and inert.
model string / claude-sonnet-5 Model used to design and implement the proposed solution.
max_attempts integer / 2 (1–10) Solution budget per issue content lineage.
max_turns integer / 200 (1–1000) Turn budget for one attempt. An attempt that exhausts it produces nothing, so raise it for a repository whose issues are routinely large — or split the issue, which is usually the better answer.
branch_prefix string / vibey-gh/issue Namespace every proposal branch lives under. Validated against the configured permanent branches and rendered into branch-intake.yml's ignore list.
base_branch string / empty Branch a solution is built on. Blank uses branches.integration.
solve_untrusted_authors boolean / false Permit issues from outside the owner/trusted-author set without a maintainer label.
required_label string / vibey-gh:solve Label that opts an outside author's issue in. Empty disables that path entirely.
trigger_labels string list / empty When set, only issues carrying one of these labels are ever attempted.
ignored_labels string list / question, discussion, duplicate, wontfix, vibey-gh:solve-blocked Issues carrying one of these are never attempted, whoever wrote them.
open_pull_request boolean / true Open a linked pull request after publishing the branch.
draft_pull_request boolean / true Open that pull request as a draft, letting PR automation promote it when its exact head is green.
retain_schedule_backstop boolean / true Retain the scheduled recovery sweep beside the event triggers.
fallback_enabled boolean / false Post a bounded local-model triage comment when the paid solve produced nothing — the issue path's counterpart to [pr_automation.fallback], sharing its runner, model, and limits. The triage writes no code, deduplicates itself to one comment per issue, and forces needs_human true whatever the model claims. Off by default because it needs that self-hosted runner to exist.

An issue's attempt budget is keyed to a SHA-256 fingerprint of its title and body, so re-running automation on unchanged text cannot spend the budget twice and editing the issue starts a fresh lineage. Managed labels are vibey-gh:solve, vibey-gh:solving, vibey-gh:solution-proposed, vibey-gh:solve-exhausted, and vibey-gh:solve-blocked.

Documenting your project, not this one

A repository that installs vibey-gh documents its own product. It is still held to the agent-docs layout — those files describe the adopter's project and make it navigable to an agent — but nothing about their contents describes vibey-gh: no ## Why vibey-gh heading in their product README, no branded provenance sentence, no architecture surfaces named after this tool's modules.

Every entry in required_files is required: having one never excuses another.

Field Type / default Meaning
required_files string list / the agent-docs layout Files that must exist and be non-empty, each one individually.
require_roadmap boolean / true The living-roadmap doctrine (#211): docs/roadmap.md or ROADMAP.md must exist and be non-empty until the project's goal is reached and its humans declare it done. Opting out silences only this deterministic presence check — the exact-head review still judges roadmap liveness against the release history.
readme_sections string list / empty Headings required in README.md, in your own words.
automation_doc path / .github/AUTOMATION.md Where this repository's automation documentation lives. Not .github/README.md — GitHub resolves that as the repository's landing README ahead of the root one, so naming it that replaces your product README on your repository's front page.
automation_doc_sections string list / empty Headings required in automation_doc. Also read from the former name github_readme_sections.
automation_doc_min_words integer / 0 Minimum length for automation_doc; 0 disables. Also read from the former name github_readme_min_words.
mermaid_terms string list / empty Surfaces that must appear in docs/project.mmd.
mermaid_min_edges integer / 0 Minimum --> edges in that diagram; 0 disables.
require_provenance boolean / false Require the Vibey provenance sentence.
provenance_files string list / README.md, docs/index.md Files checked for the sentence with a loose substring match (must merely contain "Made with" somewhere), skipped if the file does not exist. Independently of this list, require_provenance also forces an exact-suffix check — the file must end with the sentence verbatim — on README.md and on automation_doc, whether or not either appears here.

This repository declares the full contract for itself in its own .vibey-gh.toml, which is both the dogfooding rule the rest of the tool follows and the reason its own requirements are visible rather than compiled in.

[tidy]

The clean repo (sub-doctrine 9.a): every repository is kept technically clean at all times — no exceptions — locally and in the cloud, while human messiness is expressly welcome and never touched: prose, discussions, stashes, work in progress. The clutter this wars on is machine-state clutter only.

Field Type / default Meaning
enabled boolean / true The doctrine's bar; disabling records a deliberate exception in review.
keep_branches string list / empty Kept beyond the integration and release branches (an LTS line, say).
trust_forge_deletions boolean / true Squash and rebase merges rewrite SHAs, so ancestry cannot prove a merged branch landed — the forge deleting its remote at merge time is the proof instead. Set false where remote branches die for other reasons.

Losslessness governs every deletion: ancestry-contained refs and forge-deleted upstreams only. Anything not provably redundant — draft releases, orphan tags, stashes, untracked paths — is reported to the human and never machine-removed.

[social_signals]

The social-signals surface (sub-doctrine 4.a, ratified by operator merge): the published site presents real human social proof — testimonies, endorsements, adoptions, case studies, reviews, community counts, citations, press, contributors, backers, talks, certifications — as a self-contained, theme-aware section on the landing page, injected after the site build.

The sub-doctrine's terms are permanent and configuration cannot soften them:

  • Opt-in, forever available. Off by default in every repository; the capability itself exists at all times, in all places, with no exceptions — an adopter turns it on, never asks whether it exists.
  • 100% comprehensive to the day. The kind taxonomy covers the authentic social-signal classes of the current world and grows when the world grows a real new one — never a synthetic one.
  • Verification expires (the 4.a amendment). A past-authentic signal is never assumed presently authentic — man-in-the-middle and forgery attacks target exactly that assumption. Every authenticity claim is only a claim, including the operator's own and especially the agent running the code; every entry carries the date a human last verified it, stale attestations block until re-verified, and a signal discovered inauthentic is removed immediately and permanently — revoked entries can never be re-attested.
  • 100% authentic, from a real human agent, never a machine. Every entry names its agent — a person, or an institution of persons such as a government — carries its source hyperlink at the point of reference, and carries human_attested = true: the operator's own attestation, made by the human who added the entry. Validation refuses anything less at config load, and the exact-head review blocks machine-authored, synthetic, unattributed, or unverifiable signals as false witness.
[social_signals]
enabled = true
heading = "Real people, real words"

[[social_signals.entries]]
kind = "testimony"                     # see the kind taxonomy above
agent = "Jane Doe"                     # the real human agent — always named
role = "CTO"
org = "Acme"
date = "2026-08-30"
quote = "It shipped my release while I slept."
source = "https://example.com/jane-said-it"   # provenance, at the point of reference
human_attested = true                  # the operator's own attestation — required

[[social_signals.entries]]
kind = "community"
agent = "GitHub stargazers"
value = "1,204"
source = "https://github.com/you/repo/stargazers"
human_attested = true
Field Type / default Meaning
enabled boolean / false Opt in per repository; the feature itself is always available.
heading string / "Real people, real words" The section heading on the landing page.
max_attestation_age_days integer / 365 The 4.a amendment's re-verification clock: an attestation older than this blocks the check until a human re-verifies the signal and re-dates it. Must be positive — attestations that never age are forbidden.
entries array of tables / none Each entry: kind (from the taxonomy), agent, source (https), human_attested (required true), attested_on (required ISO date — when the human last verified authenticity), and optionally quote, role, org, date, value, revoked.
entries[].revoked boolean / false The permanent tombstone: a signal discovered inauthentic renders nowhere, forever, and can never be re-attested — revoked with human_attested is refused at load, no exceptions ever.

Voiced kinds (testimony, endorsement, review, press, case-study, talk) render as quote cards; counted and named kinds render as compact linked chips. The section closes by saying what it is: every entry is attested human speech — never a machine's.

[yank]

Report which releases on an index the just-published version supersedes.

It reports. It cannot yank, and neither can anything else you write. PyPI exposes no API for yanking. The legacy upload endpoint answers 405 Method Not Allowed for :action=yank (a recognised action such as :action=file_upload answers 403 on bad credentials, so authentication is never even reached), and the /manage/... route the web UI uses is CSRF-protected against non-browser callers. Programmatic access is an open upstream request, not a shipped capability:

PyPI's own documentation gives exactly one method: the release management page, Options → Yank. No token changes this; do not try to add one.

That is arguably the right design. PEP 592 defines a yanked release as one with "a serious problem which should prevent it from being installed" — a distress signal, not a tidiness marker. Installers still resolve a yanked version when a pin demands one, so nothing is reclaimed; what changes is that everyone pinned to it starts seeing a warning about a release that may be perfectly good. The manual click is the friction that keeps that deliberate.

So this automates the analysis and leaves the click: it works out exactly which releases are superseded and prints them with a link to the page that can action them.

Field Type / default Meaning
pypi boolean / false Report superseded PyPI releases after a publish.
testpypi boolean / false Report superseded TestPyPI releases.
keep integer / 0 How many releases below the newest to leave out of the report, so a rollback target is never suggested.
governance_paths string list / the founding documents fnmatch globs (default docs/constitution.md, docs/commandments.md, docs/bill-of-rights.md, docs/sd-*.md). Article V.4 of the Constitution: when a release's range touches any of them — a RATIFIED governance change — every previous release on both indexes is reported superseded, with keep and the two switches above overridden. Zero exceptions: the config is machinery and the rule is law. Pass the ratifying push's range as --governance-since (see below).

Two invariants hold regardless of configuration:

  • the version just published is never listed, excluded by identity rather than by version ordering;
  • a version this cannot parse is never listed. Ordering covers N.N.N and N.N.N.devN, which is what this tooling publishes. Epochs, local segments, post- and pre-releases are left out, because half a PEP 440 parser mis-orders them silently and here that means naming a good release as a candidate for yanking.

Run it from a release workflow after the upload step. No credentials are involved — the index JSON it reads is public:

vibey-gh report-superseded --index pypi --project my-package --version "$VERSION" \
  --governance-since "$GITHUB_EVENT_BEFORE"   # optional: evaluate Article V.4 on the push range

--governance-since takes the git ref that opened the release range (a push event's before SHA). If the range touches a governance_paths file, the report is the Article V.4 demand: every previous release named, retention window and switches overridden. An unreadable ref is reported loudly — the rule is never silently waived — but never fails the release.

It always exits 0: the package is already published by the time it runs, so a bookkeeping failure is reported rather than turning a successful release red.

Machine-level: ~/.config/vibey-gh/failover.toml

The operator-seat failover engine (vibey-gh failover, #208) is configured per machine, never per repository — seats describe the operator's laptop, so nothing about them belongs in a repository's .vibey-gh.toml. A missing file is a disabled engine; nothing here ever self-activates.

enabled = true
paid_probe = "claude -p ok --max-turns 1"   # exit 0 = the paid lane is alive
interval_seconds = 300

[[seats]]                                    # tried in order; first healthy one wins
name = "qwenloop"
launch = "qwenloop run"
health = "curl -sf http://127.0.0.1:11434/api/tags"

[[seats]]
name = "opencode"
launch = "opencode"                          # empty health = engage without preflight
Field Type / default Meaning
enabled boolean / false The operator writes true deliberately; the first live handoff should be supervised.
paid_probe string / empty A shell command whose exit status answers "is the paid lane alive?" — the 296 ms Credit balance is too low refusal is exactly what it distinguishes from health. A hang counts as down.
interval_seconds integer / 300 Loop cadence when run without --once.
seats array of tables / qwenloop, then opencode Each seat is a name, a launch command, and an optional health preflight, judged by exit status — any agent fits without a code change.

Seat state (which agent holds the seat, and its pid) lives in ~/.local/state/vibey-gh/failover.json; --config and --state override both paths. The handoff is lossless because the seats share one working tree and the local-authority loop keeps local and remote synced throughout.

[github_release]

Field Type / default Meaning
enabled boolean / true Enable immutable tags and GitHub Releases.
tag_prefix string / v Nonempty, whitespace-free tag prefix.
generate_notes boolean / true Ask GitHub to generate release notes.
require_new_version boolean / false Fail instead of silently doing nothing when a release-branch push does not carry a new version (the tag it would need already exists at a different commit). Leave off for a repository where a docs-only or tooling-only promotion is a normal, frequent, versionless push.

[rulesets]

Reconciles GitHub repository rulesets for the integration and release branches, so the protection repository-profile.yml has always only verified is actually set. Branch names are not configured here — [rulesets.integration] always targets branches.integration and [rulesets.release] always targets branches.release.

Field Type / default Meaning
enabled boolean / true Reconcile both rulesets at all. false leaves the repository untouched, exactly as before this feature existed.

[rulesets.integration] and [rulesets.release]

Field Type / default Meaning
required_checks string list / integration: ["Provenance", "Analyze Python", "Documentation contract", "PR automation / gate"]; release: the same without the gate Required status-check contexts — check-run names, not workflow names (see below). Empty omits the check requirement entirely.
strict_required_checks boolean / true Require the branch to be up to date with its base before merging.
required_approvals integer / integration: 0, release: 1 (0–6) Required approving reviews. Integration defaults to 0 because PR automation gates it instead.
dismiss_stale_reviews boolean / true Dismiss stale reviews when new commits are pushed.
require_conversation_resolution boolean / true Require every review thread to be resolved before merging.
require_linear_history boolean / true Forbid merge commits onto the branch.
require_signed_commits boolean / false Require every commit to be signed.
allow_force_pushes boolean / false Rejected at load time if true. A permanent branch can never be configured to allow force pushes.
allow_deletions boolean / false Rejected at load time if true. A permanent branch can never be configured to allow deletion.
bypass_actors string list / ["RepositoryRole:5"] "<ActorType>:<id>" entries granted to bypass the ruleset. The default is the repository admin role. [] means nobody — including the owner.

required_checks names check runs, not workflows

This is the one field here that can lock a branch with no way out, so it is worth stating plainly. A required status check matches a check run, and for GitHub Actions a check run is named for its job, not its workflow. The CI workflow in this repository reports as Lint, Build, and Test (3.12); nothing ever reports as CI.

Requiring a name nothing produces does not fail — it waits. The branch reports N of M required status checks are expected forever, and because a ruleset has no "include administrators" toggle the way classic branch protection did, an empty bypass_actors means no one can merge past it. The only exit is editing the ruleset.

Two habits avoid it: name the job, and keep a bypass actor. [pr_automation].scan_workflows names workflows and looks like a tempting list to reuse here — it is not one. To find the real names, open a recent pull request's checks tab, or:

gh api "repos/OWNER/REPO/commits/$(git rev-parse HEAD)/check-runs" \
  --jq '.check_runs[].name' | sort -u

Reconciliation is idempotent read-compare-write, the same shape repository-profile.yml already uses for settings and topics: an existing rule type the configuration does not mention is never removed, only reported. A ruleset the API refuses fails the job with the API's own reason rather than being silently skipped — a skipped reconciliation would look identical to a satisfied one. Run vibey-gh rulesets --dry-run to inspect the diff before a workflow run applies it.

[repository_profile]

Field Type / default Meaning
enabled boolean / true Reconcile repository settings.
description string / derived Description, at most 350 characters.
topics string list / five automation topics Lowercase topics, maximum 20.
has_issues, has_projects, has_discussions boolean / true Enable collaboration features.
has_wiki boolean / false Enable the wiki.
allow_squash_merge, allow_rebase_merge, allow_auto_merge boolean / true Allowed merge mechanisms.
allow_merge_commit boolean / false Permit merge commits.
delete_branch_on_merge boolean / false GitHub's own blanket auto-delete-on-merge. Keep false because develop heads promotion PRs and would itself be deleted. This is independent of branch cleanup: the merge train and Automation bootstrap already delete a merged PR's head branch themselves, through a guarded API call, whenever it is not a permanent, integration, or release branch and not a fork — regardless of this setting.
web_commit_signoff_required boolean / true Require web-editor signoff.
vulnerability_alerts, automated_security_fixes boolean / true Enable dependency security services.

[documentation]

Field Type / default Meaning
enabled, ai_maintenance boolean / true Require and AI-maintain the documentation suite.
model string / claude-sonnet-5 Documentation model.
required_files string list / built-in FOSS and agent suite Required documentation paths.
production_label, preview_label strings / Production, Preview Human-facing channel names.
production_indexing boolean / true Permit production indexing.
preview_indexing boolean / false Permit preview indexing.
generate_robots, generate_sitemap_index, generate_llms_txt, generate_llms_full_txt, generate_json_ld boolean / true Generate robot, search, LLM, and structured metadata.
funding_bitcoin, funding_monero, funding_ethereum string / empty Opt-in funding signage (#198): when set, a small footer line beside the provenance signage offers the address as text with a copy button — never a payment-processor link. Each address is shape-validated at config load (bech32/Base58 for Bitcoin, 95/106-char Monero base58, 0x+40-hex for Ethereum); a malformed value is a configuration error, and no default ever ships. The render is verbatim from this file, so any change to an address is a reviewed, fingerprinted commit.
funding_label string / Support this work The sentence introducing the funding line.
bottom_nav boolean / true Clone the theme's own rel="prev"/rel="next" header anchors into a previous/next bar at the bottom of every published page, so a reader who has just finished a page — especially on a phone — can move on from where they already are. Pages without those anchors (the channel picker, 404) get no bar. false disables the injection.
author_name string / Adam Matthew Steinberger Reserved documentation-provenance author label. Parsed and validated (non-empty), but not yet emitted into any generated asset.
author_url URL / https://vibewithadam.matthewsteinberger.com Reserved documentation-provenance author destination. Same current scope as author_name.
google_analytics_id string / empty (disabled) GA4 measurement ID (G-<alphanumeric>) injected into every page of both generated documentation channels and the channel-picker page. Empty disables Google Analytics entirely: no script tag is emitted and no request ever reaches Google.
favicon string / 📘 One or two emoji render as a zero-asset SVG favicon (plus a matching apple-touch-icon). A value that starts with http://, https://, or /, or whose last path segment contains a ., is instead used verbatim as a <link rel="icon"> URL. Empty omits the favicon link.
og_image URL / empty Social preview image rendered into the Open Graph and Twitter Card meta tags on every generated page. Empty falls back to GitHub's own generated OpenGraph card for the release commit, which always exists and stays current.
twitter_site string / empty @handle rendered as the twitter:site meta tag. Empty omits the tag.
twitter_creator string / empty @handle rendered as the twitter:creator meta tag. Empty omits the tag.
keywords string list / empty Rendered as the page's <meta name="keywords"> and, when generate_json_ld is enabled, the JSON-LD keywords property. Empty falls back to [name, owner, "documentation", "release notes", "changelog"]. Entries must not contain <, >, ", a comma, or a newline.
author string / empty Rendered as the page's <meta name="author"> and, when generate_json_ld is enabled, the JSON-LD author.name. Empty falls back to the repository owner. Distinct from author_name/author_url below, which are not yet emitted anywhere.
theme_color hex colour / #080b14 Rendered as <meta name="theme-color"> when non-empty. Must match ^#[0-9a-fA-F]{3,8}$.
locale string / en_US Rendered as og:locale and, when generate_json_ld is enabled, the JSON-LD inLanguage (with _ replaced by -).
google_site_verification string / empty Google Search Console "HTML tag" verification token — the bare content= value, not the whole <meta> tag; must match ^[A-Za-z0-9_-]{1,128}$. Rendered as a <meta name="google-site-verification"> tag on every published page and the channel-picker index, so it survives Pages redeploys, unlike an uploaded verification file.
site_requirements string list / empty Extra packages installed before the published site is built, as PEP 508 requirement specifiers. Each is shell-quoted, so "mkdocs-material[imaging] >= 9.5" stays one argument.
site_requirements_file path / docs/requirements.txt Installed with pip install -r when the file exists. Absent, the step is skipped; empty disables the hook entirely.
properdocs_version string / 1.6.7 The properdocs and properdocs-theme-mkdocs version the site build pins.

Installing what your site actually needs

ProperDocs depends on properdocs and its theme, and on nothing your properdocs.yml declares. A site configuring mkdocs-gen-files, mkdocs-literate-nav, a Material theme, or any pymdownx.* markdown extension needs those packages present, or the --strict build fails on the first one it reaches — the plugin is simply not installed.

Declare them once, either inline or in the conventional requirements file:

[documentation]
site_requirements = [
  "mkdocs-gen-files",
  "mkdocs-literate-nav",
  "pymdown-extensions>=10.7",
]

Both hooks are no-ops when unused, so a repository whose site needs nothing extra is unaffected. This cannot have a useful default: which packages a site needs follows from that site's own configuration.

author_name and author_url exist for a planned author credit in the generated Pages sites and are exercised by config parsing, validation, and tests today, but release-surfaces.yml never reads either one, so setting these two keys has no visible effect on a generated site yet. This is unrelated to author above: that field is already wired into the rendered <meta name="author"> tag and the JSON-LD author.name property.

favicon, og_image, twitter_site, twitter_creator, keywords, author, theme_color, and locale land verbatim in rendered HTML and workflow YAML, so each is validated at load time rather than discovered on a published page: none of the string-valued fields may contain <, >, ", or a newline, keywords entries additionally reject commas, and theme_color must match a hex colour pattern.

Run vibey-gh install, review and commit generated assets, then run vibey-gh check --ci. Identity and Pages URLs are derived at runtime.

Advanced debug environment

Variable Default Meaning
VIBEY_GH_DEBUG unset Set to 1, true, yes, or on to enable structured branch tracing.
VIBEY_GH_DEBUG_LOG stderr Append JSONL trace events to this operator-controlled path.
VIBEY_GH_TRACE_ID generated UUID Correlate the trace with a wider diagnostic session.

GitHub correlation is read from GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT, and GITHUB_SHA. These controls affect diagnostics only; source validation always confirms that every configured Python control-flow opcode can be represented by the tracer.

These environment variables only ever scope the tracer to vibey_gh's own installed package directory; there is no CLI flag or environment variable to point it at a consuming project's source tree. A project embedding vibey_gh.debugging directly can call enable(roots=(...)) with its own package directories to trace its own code instead.