Burin Labs organization defaults and reusable GitHub Actions workflows.
The documentation checker action validates Harn examples using repository path rules and the checker maintained by Harn. It checks syntax, types, formatting, and lint rules, and refuses a scan with no checked examples.
burin-labs/.github/.github/actions/local-workflow-checks runs the commands
already declared in a repository's GitHub Actions workflow. The repository's
versioned .github/local-checks.json adds local-only facts such as platform
support, setup steps, remote-only steps, and shared build locking; it does not
copy commands out of the workflow.
The runner reports every passed, failed, unavailable, setup, and remote step. An incomplete workflow census and a run with zero measured checks both fail. See Local workflow checks for the policy contract, local command, and pinned action syntax.
burin-labs/.github/.github/actions/rust-test-impact is the organization-owned
adapter for branch-only Rust test impact analysis. It reads Cargo's real
workspace graph, expands directly changed packages through the transitive
reverse-dependency closure, and emits full, partial, or empty plus Cargo
package arguments. Merge queues, main pushes, schedules, workflow changes,
workspace manifests, lockfiles, and unresolved diffs retain the complete
workspace.
Repositories provide only the Cargo workspace path and genuinely global inputs; they do not maintain crate dependency tables. Keep the action pinned to an exact commit and pass exact pull-request base/head SHAs.
The planner compares those two exact trees directly. A branch behind its base may over-select packages changed on main, but cannot omit a package changed by the pull request; this keeps shallow checkouts fast without weakening coverage.
- id: rust-impact
uses: burin-labs/.github/.github/actions/rust-test-impact@<full-commit-sha>
with:
event-name: ${{ github.event_name }}
base: ${{ github.event.pull_request.base.sha || github.sha }}
head: ${{ github.event.pull_request.head.sha || github.sha }}
workspace: crates/my-workspace
extra-package-paths: |
my-api=docs/openapi
- run: cargo nextest run ${{ steps.rust-impact.outputs.package-args }}burin-labs/.github/.github/actions/exact-tree-ci-proof is the organization-owned
fail-closed adapter for skipping already-proven jobs on a merge group or landing
push when the git tree matches the source pull request. Repositories name the
required jobs and a cache-contract refresh bit; they do not copy the GitHub API
helpers.
Source pull requests produce proof. Merge groups and main pushes consume it
only when every named job concluded success (not skipped). A
cache-contract change keeps the push writer running. Restore-only merge
groups (the default) still reuse: a rebuild there produces nothing.
Pass merge-group-writes-cache: "true" when the merge-group job persists a
cache, such as a sticky disk. Lookups fail closed.
- id: rust-proof
uses: burin-labs/.github/.github/actions/exact-tree-ci-proof@<full-commit-sha>
with:
workflow-file: ci.yml
required-jobs: >-
["Rust TUI fast fmt, clippy & test","Rust TUI harn-linked clippy, test & build"]
cache-refresh-required: ${{ steps.filter.outputs.rust_cache_contract }}
merge-group-writes-cache: "true"
event-name: ${{ github.event_name }}
commit-sha: ${{ github.sha }}
event-path: ${{ github.event_path }}
github-token: ${{ github.token }}.github/workflows/runner-availability.ymldetects idle self-hosted Linux, macOS, and Windows runners and falls back to GitHub-hosted runners when the pool is busy, unavailable, or inaccessible from a fork/dependabot run..github/workflows/harn-package.ymlchecks out a Harn package, installs its exact.harn-version, runs the Harn-owned package contract, and uploads the structured receipts..github/workflows/register-package-release.ymltells the package index that a release shipped, so the index reconciles within minutes instead of at the next daily run.
A release tag and the Harn package index are two facts that have to agree, and
nothing made them agree: harn publish is author-invoked and wired into no
release pipeline, so @burin/github-connector served 0.3.0 while v0.8.3 had
shipped. See burin-labs/harn-github-connector#289.
burin-labs/harn-packages owns what the index should say; its reconciler
compares every indexed package against git ls-remote and the published
harn.toml, and proposes the corrections it can derive. This workflow is only
how a package repository tells the reconciler that something changed, which is
why it is one call rather than a copy of the mechanism in every package
repository:
on:
push:
tags: ["v*.*.*"]
jobs:
register:
uses: burin-labs/.github/.github/workflows/register-package-release.yml@<full-commit-sha>
secrets:
RELEASE_APP_CLIENT_ID: ${{ secrets.RELEASE_APP_CLIENT_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}Call it as a dependent job in a release workflow where one exists, so an unregistered release fails the release run. The token it mints can dispatch the reconciler and read the resulting run; it cannot write the index, which stays with the reconciler in the repository that owns it.
.github/runner-fleet.json is the typed inventory of Burin Labs-owned runners.
The hourly Self-hosted runner health workflow takes two read-only GitHub
snapshots one minute apart and fails only when the same expected runner remains
missing or offline in both. Exact names exclude Blacksmith's short-lived runner
registrations. The workflow uploads a compact seven-day report and never
restarts a host; machine-local remediation remains an explicit idle-only
operation.
An open Dependabot alert must mean "a human should act". Alerts that cannot be actioned — because upstream has published no patched version — otherwise sit open forever, and a queue that is permanently non-empty stops being read.
The policy, in order of preference:
- Fix it. If an installable patched version exists, the alert is a task, not noise. File it and leave the alert open.
- Let GitHub triage it. GitHub's preset auto-triage rules are available on
every repository, including private ones, and are the right tool when they
apply. The
Dismiss low impact issues for development-scoped dependenciespreset only matches npm dependencies atscope: developmentcarrying one of a curated CWE list, so it does not cover runtime-scoped findings. Custom auto-triage rules — which support asnooze until a patch is availableaction, the self-healing behaviour we want — require GitHub Code Security on private repositories and are UI-only; there is no REST or GraphQL API for them, so they cannot be provisioned from a workflow. - Waive it, with evidence and an expiry. Dismiss the alert with the closest
accurate GitHub reason and record a waiver in the affected repository at
.github/dependabot-waivers.json.
Do not use a dependabot.yml ignore entry for this. ignore suppresses
Dependabot's pull requests, not its alerts — "There is no interaction
between the settings specified in the dependabot.yml file and Dependabot
security alerts". It would leave the alert open and simultaneously block the
future fix, which is the same class of failure as an exact-version override.
GitHub only auto-reopens alerts that one of its own auto-triage rules
dismissed, and only when "the alert metadata or rule changed". An alert
dismissed by a human or through PATCH /repos/{owner}/{repo}/dependabot/alerts/ {alert_number} is never resurfaced — including on the day upstream finally
ships the fix. Nothing in a dismissal expires by itself.
scripts/dependabot_waivers.rb closes that gap. The weekly
Dependabot waiver audit workflow reads every repository's waiver file,
compares it against the live alert, and reopens the alert when the waiver
stops being true: an installable fix appeared, the severity rose, or the alert
now points at a different advisory. It fails the job when a waiver has gone
stale or has passed its review_by date, so the registry cannot quietly become
the new place noise accumulates.
Checking in a registry opts the repository into the policy: from then on the audit also fails on a dismissed alert that has no waiver, since such a dismissal records no reason and never expires. Repositories without a registry are skipped entirely.
Some alerts have a published fix that this dependency graph cannot adopt,
because a third party caps the version. tui-textarea requires
ratatui ^0.29, and ratatui 0.29 is the only thing still pulling the
vulnerable lru; unsloth requires torch<2.12, while the patch is 2.13.
These are not fixable today and are not "no patch exists" either.
Such a waiver sets observed.fix_available: true and must add
observed.blocked_by, naming the blocking package, the version and requirement
read from it, the edge it constrains, and override_rejected: why forcing
the fix past the cap is not viable. That last field is required because almost
any cap can be overridden, and whether that is safe is a judgement no schema
can make — so it has to be made in writing and reviewed.
For these, the audit re-reads the cap, not the patch: any change to what the
blocking package publishes reopens the alert. It deliberately does not attempt
version-range arithmetic across three ecosystems' dialects; a changed
requirement puts a human back in the loop. Note constrains is often not the
vulnerable package — tui-textarea caps ratatui, and ratatui is what drags
in lru.
This is the one exception to "an available fix means it is a task, not a waiver", and it is narrow on purpose. If the fix is reachable at all, even awkwardly, leave the alert open and file it.
Crucially, the audit checks whether a patched version is installable, not
whether the advisory claims one. GitHub's first_patched_version is an
advisory claim about a product release; GHSA-x744-4wpc-v9h2 names Docker
"29.3.1" while no such version of the github.com/docker/docker Go module is
published anywhere. Trusting the claim would reopen that alert on every run.
Copy templates/dependabot-waivers.json into the affected repository at
.github/dependabot-waivers.json and check it with:
ruby scripts/dependabot_waivers.rb verify .github/dependabot-waivers.json owner/repoThe registry deliberately lives in the repository it describes, not here: this repository is public, and a central list of unpatched vulnerabilities in private repositories would publish an attack surface. Each waiver must carry prose explaining why the alert cannot be actioned and the evidence for it, because GitHub has no dismissal reason meaning "upstream shipped no patch" — the checker rejects a waiver that omits it, and rejects one whose own recorded evidence admits an available fix.
The workflow is gated on the DEPENDABOT_WAIVER_AUDIT variable and stays
skipped until two things a workflow cannot do for itself are done by hand:
- Grant the
harn-release-botGitHub App the Dependabot alerts: read and write repository permission (App settings → Permissions), and accept the permission change on the organization installation. Reopening an alert is a write; nothing less will do. - Set the repository or organization variable
DEPENDABOT_WAIVER_AUDITtoenabled.
Until then the job reports as skipped rather than running red every week, since a permanently failing alarm is the failure mode this whole mechanism exists to prevent.
burin-labs/burin-example-* repositories are eval fixtures, not products.
The Burin eval harness provisions them to ~/projects/burin-examples/<lang> and
measures agent behaviour against those trees.
The root cause is on the consumer side, not here. The harness consumes a
fixture at the branch tip, never at a pinned revision:
clone-golden-fixture.sh runs git clone --depth=1 --single-branch with no
ref, clone-eval-fixtures.sh --update runs git pull --ff-only, and the
revision recorded at grading time (fixtureRevision) is a rev-parse HEAD
observation that is never compared against an expected value. So every commit on
a fixture's default branch silently moves the ruler, and two machines that
provisioned on different days measure different trees under the same cell name.
Dependabot was merely the loudest writer. burin-example-ruby took 63 Dependabot
pull requests, and 28 of the 39 commits on its main since 2026-05-01 were
dependency bumps. A human fixing a typo is the same hazard, smaller.
Pinning fixture revisions at the consumer is the durable fix and is tracked in
burin-labs/burin-code#6935. Everything below is a stopgap with an explicit
expiry, not the design.
- No
.github/dependabot.ymlin a fixture. Thetemplates/dependabot.ymlprojection andcheck-dependabot-configdo not apply. The file's presence is the only switch for version updates, and pure version bumps are noise against a fixture under any pinning scheme. This part is permanent. - Dependabot automated security fixes disabled at repository settings
(
DELETE /repos/{owner}/{repo}/automated-security-fixes). This is what produces Dependabot pull requests in a repository that has no config file at all, and it is the setting most people miss. This part is temporary. - Dependabot alerts stay enabled. The vulnerability signal is kept; it is only forbidden from opening a pull request by itself.
Nothing security-relevant was suppressed when this was applied: across all 19
fixtures there were 0 open Dependabot pull requests and 0 open Dependabot alerts.
The 163 alerts in fixture history are all fixed, apart from 5 dismissed by hand
as not_used.
Once burin-code#6935 lands and fixture revisions are pinned at the consumer,
reproducibility no longer depends on freezing these repositories, and security
updates should be turned back on:
gh repo list burin-labs --topic eval-fixture --limit 200 \
--json name -q '.[].name' |
while read -r r; do
gh api -X PUT "repos/burin-labs/$r/automated-security-fixes"
doneSeveral of these repositories are public and are read, copied, and trained on. A fixture carrying a knowingly vulnerable pin teaches the wrong pattern, so the resting state is security-only Dependabot on a SHA-pinned fixture — not silence. Version updates stay off either way.
New fixtures are born quiet: the organization's
dependabot_security_updates_enabled_for_new_repositories and
dependabot_alerts_enabled_for_new_repositories defaults are both off. That is
the current default, not the target one; a fixture created after #6935 should be
given security-only Dependabot deliberately.
Every fixture carries the topics eval-fixture and no-dependabot, which make
the membership of this class one query rather than a naming guess:
gh repo list burin-labs --topic eval-fixture --limit 200 --json nameAuditing the posture, when you want to confirm it rather than trust it:
gh repo list burin-labs --topic eval-fixture --limit 200 \
--json name -q '.[].name' |
while read -r r; do
fix=$(gh api "repos/burin-labs/$r/automated-security-fixes" --jq '.enabled')
cfg=$(gh api "repos/burin-labs/$r/contents/.github/dependabot.yml" \
--jq '.path' 2>/dev/null) || cfg=none
printf '%-26s autofix=%s config=%s\n' "$r" "$fix" "$cfg"
done.github/actions/ci-latency-policy checks a repository's
.github/ci-latency.json against its CI workflow. The policy names the
end-to-end SLO, the jobs that distinguish a full run from a fast lane, and a
budget for every job required by the aggregate status job.
Call the action from an existing source-only CI job so the check does not add a runner:
- uses: burin-labs/.github/.github/actions/ci-latency-policy@<full-commit-sha>
with:
baseline-sha: >-
${{ github.event.pull_request.base.sha ||
github.event.merge_group.base_sha || '' }}
github-token: ${{ github.token }}The token needs contents: read only. When baseline-sha is present, the
action reads the policy at that commit and rejects increases to the
critical-path allowance or an existing job budget. It also rejects missing or
stale required-job entries, empty sentinel sets, invalid SLO ordering, and a
same-topology observed baseline that is removed, tampered with, or loosened.
Runtime measurement belongs off the pull-request path. The organization-level
observer runs every six hours and on demand. It uses Harn's
scripts/ci_walltime_report.harn implementation and the existing release-app
installation token with actions: read and contents: read. It runs on this
public repository's standard Linux runner, so observing private repositories
does not consume their hosted-runner minutes. The observer executes no code or
artifacts from measured workflow runs. It emits a job summary, retains compact
JSON reports for seven days. Product-target misses are first-class target-debt
receipts and remain visible without making every schedule red. The observer
fails on a sustained p90 regression past the reproducible observed baseline or
one run past its max-latency regression fuse. Missing, stale, invalid, or empty
evidence also fails closed. A same-topology baseline may only tighten.
.github/actions/ci-runtime-evidence collects a versioned JSON census of
completed workflow runs. Each run includes every reported job, wall and queue
time, runner class, step timing, and standard actions/cache hit or miss
messages. Event and branch quotas stay with the calling repository.
Run it from a scheduled workflow and pin the action to an exact commit:
- id: runtime-evidence
uses: burin-labs/.github/.github/actions/ci-runtime-evidence@<full-commit-sha>
with:
harn-version: 0.10.133
github-token: ${{ secrets.ACTIONS_READ_TOKEN }}
repository: owner/repository
workflow: ci.yml
queries-json: >-
[{"event":"pull_request","count":100},
{"event":"push","branch":"main","count":100}]
output: .harn/ci-runtime-evidence.jsonThe token needs Actions read access to the measured repository. The action
installs the selected Harn release, requires unzip, downloads each run's
GitHub-generated log archive once, and fails when run or job evidence is short,
partial, duplicated, or internally inconsistent. A skipped job remains a
measured job with null execution timing. Omit harn-version when the calling
repository keeps its exact release in .harn-version.
Package repositories should keep the exact release in .harn-version.
Their complete CI adapter delegates package verification and rolls every
required job into one stable status check:
jobs:
package:
uses: burin-labs/.github/.github/workflows/harn-package.yml@<full-commit-sha>
status:
name: CI status
if: always()
needs: [package]
runs-on: ubuntu-latest
steps:
- uses: burin-labs/.github/.github/actions/require-successful-needs@<same-full-commit-sha>
with:
results-json: ${{ toJSON(needs.*.result) }}Product-specific verification jobs belong beside package and in the status
job's needs list. The co-versioned status action validates every dependency
result and fails on failure, cancellation, skipping, or malformed data. It
receives no dependency outputs. This prevents a green roll-up from hiding a
failed package job and replaces repository-local shell expressions with one
tested contract.
When a package needs deterministic repository-specific checks but no distinct
runner, pass them through the workflow's optional validate-command. It runs
after the canonical package contract under the same installed Harn version and
read-only permissions. Keep a separate job only when the check needs a distinct
runner, permission boundary, service, or independently visible result.
Repositories that must compose package verification into an existing job can instead use the composite action after checkout:
- uses: burin-labs/.github/.github/actions/harn-package@<full-commit-sha>
with:
strict: "true" # opt in to strict type and lint gatesThe action is a GitHub adapter only. Package policy and receipt semantics
belong to harn package verify.
On GitHub.com, the reusable workflow checks out job.workflow_repository at
job.workflow_sha under Harn's excluded .harn/ directory and invokes the
composite action from that checkout. GitHub Enterprise Server does not expose
those called-workflow identity fields, so the workflow fails early with a
specific unsupported-platform error there.
The workflow and its adapter therefore share one immutable version; there is
no second self-pin that can silently trail a newly forwarded input.
The reusable workflow delegates warning-fatal check/lint gates and strict boundary typing to Harn's canonical package contract by default. Burin Labs package CI may not opt out. The lower-level composite action keeps strict mode explicit so callers outside the organization choose their own admission policy.
Strict verification requires Harn v0.10.52 or later, where harn package verify --strict became part of the typed package contract. That release also
moves package verification receipts from schema v1 to v2 and adds the
strict_requested field on every receipt.
The daily reusable-workflow audit compares each exact pin with current workflow
content. It structurally verifies that every needs.<job>.outputs.<name> read is
declared by both the pinned and current workflow. This network-backed contract
belongs here; consumer repositories keep only offline pin-shape checks.
For callers owned by burin-labs, the package workflow also applies
.github/actions/harn-repo-policy. That action checks organization
projections—the managed agent contract, its CLAUDE.md symlink, and
connector-local guidance boundaries—while Harn owns package semantics. Public
callers receive the same package verification and receipts without inheriting
Burin Labs repository governance.
These repositories publish by pushing a tag, so the manifest version, changelog
notes, and package verification are all checked against an object that is
already immutable and signed. A gate that fails there cannot be repaired in
place: the version is burned and the tag is left with nothing behind it. That is
how harn-github-connector accumulated v0.6.1 (tagged a commit whose
harn.toml still read 0.6.0) and v0.6.7 (a release gate that only failed in
the release environment). Neither was noticed until someone went looking.
harn-repo-policy therefore asserts, on every package CI run, that every
vX.Y.Z tag has a published GitHub release, that no release is left as a draft,
and that no release outlives its tag. A tag pushed within
release-integrity-grace-seconds (one hour by default) is treated as still in
flight, so the release commit's own CI does not race the publishing job.
The check is deliberately narrow: it only recognizes vX.Y.Z, so deployment
markers and upstream naming schemes are left alone.
Use these labels when the normal merge queue or required CI status check is
the wrong tool for a rare, time-sensitive land. Labels are the trigger;
organization or repository admin membership is the authority. The reusable
workflow rejects non-admins and fork pull requests, then acts with the
harn-release-bot installation token (a ruleset bypass actor).
bypass-ci: cancel competing runs for the head SHA and publish a successfulCI statuscheck after those runs stop. Does not merge. If GitHub cannot stop a run within two minutes, the override fails closed instead of racing that run's final status.bypass-merge-queue: squash-merge immediately whenCI statusis already green. Skips the merge queue but does not override CI. An in-flight check is reported as in flight, not missing.force-merge: publish successfulCI status, then squash-merge immediately. Skips CI proof and the merge queue.
Override attempts are serialized per pull request. If several labels are
attached before an attempt starts, the workflow consumes them together and
selects the strongest request: force-merge, then bypass-merge-queue, then
bypass-ci. Its cancellation sweep excludes every run of the override
dispatcher, so concurrent label events cannot cancel one another.
- The merge queue is backed up enough that speculative CI would burn material Actions spend, and one PR must land before the rest.
- Required CI is broken in a way you can fix forward on
mainwithin the same working session. - You need to serialize a single founder land ahead of a long queue (release unblock, production incident, or similar).
- Ordinary feature work, dependency bumps, or “CI is slow today.”
- Changes you cannot fix forward if they break
main. - Pull requests from forks (the workflow refuses them).
- Confirm you are an organization owner/admin or repository admin.
- On a same-repo PR, add the label whose meaning matches the intended action.
- Read the terminal audit comment. It records the selected request, every consumed label, the re-checked actors, the actions taken, and whether the attempt was applied, refused, or errored.
- The workflow removes each consumed label on every terminal path. A label that remains attached therefore represents a pending or active attempt. For a refusal or error, follow the audit comment's reason and re-apply the named label to retry.
Organization admins can also use GitHub’s “Bypass rules and merge” UI: the
org-wide main protection ruleset grants OrganizationAdmin bypass in
pull_request mode.
Copy templates/merge-override-dispatch.yml to
.github/workflows/merge-override-dispatch.yml and pin the reusable workflow
to the full commit SHA of this repository that introduced or last changed
merge-override.yml. Keep its issues: write permission: the reusable workflow
uses the issue event history to re-check who applied every coalesced label and
removes those labels at terminal completion. Create the three labels once:
for name in bypass-ci bypass-merge-queue force-merge; do
gh label create "$name" --repo burin-labs/<repo> \
--color B60205 \
--description "Privileged merge/CI override (org admin only)" \
2>/dev/null || true
donePublic repositories stay in scope: applying a label still requires triage or higher, and the workflow re-checks organization or repository admin permission before any privileged action.
The organization ruleset requires a passing status check named CI status on
every repository's default branch. A repository whose default branch does not
exist yet cannot satisfy that, and GitHub applies the rule to repository
creation as well as to pushes. gh repo create --add-readme, the REST
auto_init flag, and template generation all return 201 Created and leave
the repository empty, with no error anywhere. Do not read a successful
gh repo create as a populated repository.
burin-labs/repo-template exists so that the first commit arrives with the
repository and every later change arrives by pull request, under the ruleset,
with nothing bypassed. It carries the CI status job, the Dependabot
projection, the pull request template, and a .gitignore.
gh repo create burin-labs/<name> \
--template burin-labs/repo-template \
--private \
--cloneUse --public instead for a repository that ships to users.
This is the step that catches a silently refused creation. Never skip it.
until gh api repos/burin-labs/<name>/commits/main --jq .sha; do sleep 3; doneGeneration is briefly asynchronous, so the first read usually returns
HTTP 409 Git Repository is empty and the next one returns the commit SHA.
A SHA means the repository is populated and protected. A 409 that never
clears means creation was refused by the ruleset; stop and fix that rather
than working around it by repointing the default branch.
The ruleset already forbids merge commits, but the repository settings should agree with it so the merge button offers only what is allowed.
gh api -X PATCH repos/burin-labs/<name> \
-F allow_merge_commit=false \
-F allow_squash_merge=true \
-F allow_rebase_merge=true \
-F delete_branch_on_merge=trueThe template lints Markdown and workflows and reports CI status. It builds
and tests nothing, because it does not know the language you are about to
write. One pull request adds all of it:
- Add a build and test job to
.github/workflows/ci.yml. - Add that job's id to the
needslist of theci-statusjob. The ruleset names one required check for every repository, so repositories extendneedsrather than renaming or replacing the check. - Add the matching ecosystem to
.github/dependabot.yml, keeping thetemplates/dependabot.ymlprojection first. - Replace the template README and add an
AGENTS.mdnaming the repository's area tags.
For a Harn package, the build job is one call to harn-package.yml. See
Reusable workflows above.
Two constraints shape this. The ruleset forbids merge commits, and a branch with unrelated history has no merge base, so GitHub will not open it as a pull request. Replay the imported history onto the new repository's initial commit instead:
gh repo create burin-labs/<name> --template burin-labs/repo-template --private
git clone https://github.com/burin-labs/<name>.git
cd <name>
git remote add imported <path-or-url>
git fetch imported
git checkout -b import imported/<branch>
git rebase --onto main --root
git push -u origin importOpen a pull request from import and merge it with rebase, so every
commit lands individually. The rebase rewrites each commit, so it also
re-signs them when commit.gpgsign is set, which the required_signatures
rule needs. Commit SHAs change; the commits, authors, dates and messages do
not.
When the original SHAs have to survive, stage the repository under a personal account, push the full history there, and transfer it into the organization. A transfer is not a ref update, so the ruleset does not see it:
gh api -X POST repos/<you>/<name>/transfer -f new_owner=burin-labsWait for the staged repository's first Actions run to appear before you transfer. A repository transferred within a second or two of its first push has arrived with GitHub Actions inert: the workflow is listed as active, no check suite is ever created, and no later push, pull request, or reopen recovers it. The required check then never reports and the first pull request is blocked with nothing to look at.
.github/pull_request_template.mdis inherited by repositories that do not carry a local override.templates/dependabot.ymlis the authoritative projection for repositories using GitHub Actions. Dependabot does not inherit organization community files, so Harn package repositories project it byte-for-byte at the start of.github/dependabot.yml; repository-specific ecosystem entries may follow. The reusable package workflow enforces the projection..github/actions/check-dependabot-configis the shared, flake-free checker for Dependabot delivery policy: every update entry needs a catch-all group (block or inlinepatterns), every committed lockfile needs a matching ecosystem entry, Cargo workspace members and path deps must stay inside the configureddirectory, and exactpnpm-workspace.yamloverrides need a# pin:annotation. It uses Ruby/Psych only — no network, no Harn binary — so always-on hygiene jobs can call it. The reusableharn-package.ymlworkflow runs it for every Burin Labs package CI caller. Product monorepos that do not use that workflow should call the action from an always-on job. Fleet prose for schedule/grouping lives inharn-bump-fleet; Harn-local family membership stays in Harn.- Repositories carrying the
eval-fixturetopic are excluded from the projection permanently, and from the automated-security-fix posture only untilburin-code#6935pins fixture revisions at the consumer. See Eval fixture repositories for why, the exit condition, and the audit command.
- uses: burin-labs/.github/.github/actions/check-dependabot-config@<full-commit-sha>
with:
# Optional anti-vacuity for repos that always ship lockfiles / overrides:
require-lockfiles: "true"
require-overrides: "true"Every Burin Labs repository titles a pull request [Area] Sentence case summary, where Area is that repository's own area tag. Each repository's
AGENTS.md lists its tags. The description is three to five sentences: what
changed, why, the one risk, and how it was verified. Do not list test commands
and do not restate the diff.
.github/pull_request_template.md here is the organization default template.
A repository inherits it only when it has no template of its own. The file is
entirely an HTML comment, so a new pull request opens with an empty body and
the author has to write something. Keep a repository-specific template only
when its area list or its own gates need saying.
CONTRIBUTING.md in this repository carries the one worked example. The
example lives there rather than in the template because template text is
prefilled into the body, and a prefilled example ships as the author's own
description when nobody edits it.
.github/actions/pr-title-check enforces the shape in CI. It reads only the
pull request's own title and body through gh pr view, so it cannot pass on
an unrelated green build. This repository runs the check on itself, so every
consumer inherits a version that has been exercised.
jobs:
pr-shape:
runs-on: ubuntu-latest
# `gh pr view` reads the pull request through the job's token.
permissions:
contents: read
pull-requests: read
steps:
# Skip the merge queue and pushes, which carry no pull request number.
- if: ${{ github.event_name == 'pull_request' }}
uses: burin-labs/.github/.github/actions/pr-title-check@<full-commit-sha>
with:
areas: "TUI|IDE|Harn bridge|Evals|Server|Site|CI"Derive the areas list from the repository's own AGENTS.md, and keep the
two in step. A tag that is in one and not the other is the failure mode this
check produces: a correct pull request blocked by a stale list.
The title must be a bracketed area tag, then a space, then a capital letter or digit. It must not end with a period. Three title shapes are exempt, because automation outside this check produces or matches them:
- A draft titled
WIP (recovered):, which marks a crash-recovered draft that was never meant to carry a real title yet. Release vX.Y.Zexactly, which release workflows match verbatim as a commit subject before they tag and publish.- Dependabot's own titles,
chore(deps...)andbuild(deps...), which Dependabot writes without reading repository rules.
The description must carry at least twenty words the author wrote. Headings, checklists, block quotes, fenced code, issue links, and generated footers are stripped before the count. That is deliberate: a body made only of template scaffolding is an unfilled template, and an earlier version of this check counted the scaffolding and passed it.
.github/actions/pr-title-check/check_test.sh covers every branch above,
including the unfilled-template case and the absent-pull-request-number case,
against a stubbed gh. CI runs it, and ci_test.rb fails if any *_test.sh
in the repository stops being reachable from a CI step.
.github/labels.yml in this repository is the canonical source for the
priority/*, status/*, and effort/* label categories every repository
shares, plus the unprefixed labels (bug, enhancement, epic,
production-readiness, and GitHub's own defaults) that already cover the
type/* role and should not be duplicated under a new prefix. area/* stays
repository-specific: each repository derives its own area labels from its
directory map.
Nothing syncs this file. GitHub does not inherit labels from an organization's
.github repository, and no workflow here writes labels to other
repositories. The file is reference that a person or an agent applies by hand.
Copy the shared categories into a repository's own .github/labels.yml rather
than re-deriving them, and rename an existing label (area:foo to
area/foo) instead of deleting and recreating it, so every issue and pull
request already carrying it stays labeled.
docs/decisions/pr-and-label-conventions.md records why these conventions
look the way they do, and which alternatives were rejected.
skills/ holds skills that apply across every Burin Labs repository. There was
no skills directory here before; this one follows the packaging convention Harn
already uses (SKILL.md with name, short, description, and when-to-use
frontmatter) rather than inventing a second shape.
skills/house-styleis the documentation style contract for every page we publish. It states rules only; the rationale and the per-repository paths live in burin-codedocs/style-guide.md, which the skill links to.