-
-
Notifications
You must be signed in to change notification settings - Fork 268
Description
Feature Suggestion
Why .DesignDocs — A Proposal for Synchronized Documentation Across the Hummingbot Ecosystem
Overview
The Hummingbot project spans several independent repositories — gateway, hummingbot-api, dashboard, and others under the Hummingbot GitHub organization. Each of these repos currently maintains its own copies of foundational documents like README.md, AGENTS.md, CODE_OF_CONDUCT.md, and CONTRIBUTING.md. Over time, these documents inevitably drift out of sync: policy updates get applied to one repo and forgotten in others, links rot, contributor guidelines diverge, and the project presents an inconsistent face to the community.
This document proposes the creation of a central .DesignDocs repository (or a top-level folder in an existing monorepo) that serves as the single source of truth for all shared documentation, and a GitHub Actions workflow that automatically publishes those documents into every tagged downstream repo.
The Problem: Documentation Drift
| Symptom | Impact |
|---|---|
CONTRIBUTING.md differs between gateway and core |
New contributors receive conflicting instructions |
CODE_OF_CONDUCT.md updated in one repo, forgotten elsewhere |
Inconsistent community standards |
AGENTS.md / CLAUDE.md AI guidance files diverge |
AI agents behave differently per repo |
TERMS_OF_USE.md is stale in some repos |
Legal and compliance risk |
README.md AI directives aren't highlighted |
In this usage, an AI focused Read Me with directions. Not the repo readme |
All of these are symptoms of the same root cause: no single authoritative source for shared documents.
The Solution: A Shared .DesignDocs Directory
Concept
- A dedicated repository (e.g.,
hummingbot/design-docs) — or the.DesignDocsfolder within an existing repo — holds the canonical versions of all cross-project documents. - Any repo that should receive those files is tagged with a GitHub topic:
hummingbot-design-docs. - A GitHub Actions workflow on the
.DesignDocsrepo watches for merges tomainand automatically opens a Pull Request in every tagged downstream repo, placing the updated files inside a.DesignDocs/directory. - Each downstream repo can still maintain repo-specific
.mdfiles inside its own.DesignDocs/folder alongside the synced ones. The publish step only overwrites the files it owns (tracked via a.managedmanifest shipped with the sync). Repo-specific files — likeGATEWAY_RULES.md— are never touched by the central sync.
Folder Structure (example)
design-docs/ ← central source repo
├── README.md ← AI directives index: lists every .DesignDocs file
│ and explains its purpose for easy AI prompt inclusion
│ (NOT the repo's public-facing README)
├── AGENTS.md ← AI coding agent guidance (all repos)
├── CLAUDE.md ← Claude-specific guidance
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── TERMS_OF_USE.md
├── CHANGELOG_TEMPLATE.md
├── MANAGED_FILES ← plain-text manifest listing every file this repo owns
.github/
└── workflows/
└── publish.yml ← the automation workflow
Each downstream repo after sync:
gateway/
├── .DesignDocs/
│ ├── README.md ← managed by design-docs (AI directives index)
│ ├── AGENTS.md ← managed by design-docs
│ ├── CODE_OF_CONDUCT.md ← managed by design-docs
│ ├── CONTRIBUTING.md ← managed by design-docs
│ ├── TERMS_OF_USE.md ← managed by design-docs
│ ├── MANAGED_FILES ← manifest shipped by design-docs; lists lines above
│ └── GATEWAY_RULES.md ← gateway-owned; never touched by the central sync
└── src/
└── ...
The MANAGED_FILES manifest (a simple newline-delimited text file) is what allows both the publish workflow and the downstream protection check to know exactly which files belong to the central repo and which belong to the downstream repo.
Documents Worth Sharing — and Why
| File | Reason to centralize |
|---|---|
README.md |
AI directives index — lives inside .DesignDocs/ only; lists every file in the folder with a short description so AI agents can reference it as a table of contents. This is not the public repo README. |
AGENTS.md |
AI coding agent guidance — must be uniform so agents behave predictably |
CLAUDE.md / CURSOR_VSCODE_SETUP.md |
Tool-specific AI context files; benefits every contributor |
CODE_OF_CONDUCT.md |
Legal/community obligation — one version, enforced everywhere |
CONTRIBUTING.md |
Onboarding document — inconsistency is the #1 contributor friction point |
TERMS_OF_USE.md |
Legal document — must be identical everywhere for compliance |
CHANGELOG_TEMPLATE.md |
Standardizes the format of release notes across the ecosystem |
Automating the Publish Step with GitHub Actions
Overview of the Flow
Push/merge to main (design-docs repo)
│
▼
GitHub Actions: publish.yml
│
├─► Discover all repos tagged `hummingbot-design-docs` via GitHub API
│
├─► For each downstream repo:
│ ├── Checkout repo
│ ├── Copy updated files into .DesignDocs/
│ └── Open Pull Request against main (or develop in GitFlow)
│
└─► Notify via Slack / GitHub Discussions (optional)
Sample Workflow: publish.yml
# .github/workflows/publish.yml
# Runs on every push to main in the design-docs repository.
# Opens a Pull Request in every repo tagged `hummingbot-design-docs`.
name: Publish DesignDocs
on:
push:
branches:
- main
workflow_dispatch: # allow manual trigger
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read # read this repo
pull-requests: write # not used here — PRs are opened via PAT below
steps:
- name: Checkout source (design-docs)
uses: actions/checkout@v4
- name: Discover tagged repositories
id: discover
env:
GH_TOKEN: ${{ secrets.ORG_PUBLISH_PAT }} # PAT with repo + PR scope
run: |
# Fetch all repos in the org tagged with our sync topic
repos=$(gh api \
"orgs/hummingbot/repos?per_page=100" \
--jq '[.[] | select(.topics[] | contains("hummingbot-design-docs")) | .full_name] | @json')
echo "repos=$repos" >> "$GITHUB_OUTPUT"
- name: Publish to each downstream repo
env:
GH_TOKEN: ${{ secrets.ORG_PUBLISH_PAT }}
REPOS: ${{ steps.discover.outputs.repos }}
run: |
BRANCH="chore/sync-design-docs-$(date +%Y%m%d%H%M%S)"
echo "$REPOS" | jq -r '.[]' | while read -r repo; do
echo "▶ Processing $repo"
# Clone the downstream repo (shallow)
git clone --depth 1 "https://x-access-token:${GH_TOKEN}@github.com/${repo}.git" downstream
cd downstream
# Copy only the files listed in MANAGED_FILES — never touches repo-specific docs
mkdir -p .DesignDocs
while IFS= read -r managed_file || [ -n "$managed_file" ]; do
[[ -z "$managed_file" || "$managed_file" == \#* ]] && continue # skip blanks/comments
cp "../${managed_file}" ".DesignDocs/${managed_file}"
done < ../MANAGED_FILES
cp ../MANAGED_FILES .DesignDocs/MANAGED_FILES # always ship the manifest itself
# Skip if there are no changes
if git diff --quiet; then
echo " No changes for $repo — skipping"
cd ..
rm -rf downstream
continue
fi
# Commit and push feature branch
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add .DesignDocs/
git commit -m "chore: sync .DesignDocs from design-docs@$(git -C .. rev-parse --short HEAD)"
git push origin "$BRANCH"
# Open Pull Request targeting `develop` (GitFlow) or `main`
gh pr create \
--repo "$repo" \
--head "$BRANCH" \
--base "develop" \
--title "chore: sync .DesignDocs shared documentation" \
--body "Automated sync from the [design-docs](https://github.com/hummingbot/design-docs) repository.
**Do not edit files inside \`.DesignDocs/\` directly** — changes will be overwritten on the next sync.
To propose changes to shared documents, open a PR in the design-docs repo instead." \
--label "documentation,automated"
cd ..
rm -rf downstream
doneRequired Setup
| Item | Details |
|---|---|
ORG_PUBLISH_PAT |
A fine-grained PAT with Contents: write and Pull Requests: write on all tagged repos. Store as an org-level Actions secret. |
| Repo topics | Each downstream repo must have the hummingbot-design-docs topic set under Settings → General → Topics. |
| Branch protection | Target branch (develop or main) should allow the bot to push via the PAT, or use a bypass rule for the Actions user. |
MANAGED_FILES manifest |
A newline-delimited file at the root of the design-docs repo listing every file the central repo owns. Shipped into .DesignDocs/MANAGED_FILES on every sync. Both the publish workflow and the downstream protection check use this file. |
Protecting Managed Files in Downstream Repos
A valid concern: what happens if a contributor in the gateway repo opens a PR that accidentally (or intentionally) edits a centrally-managed file like AGENTS.md? The next sync would overwrite their change, silently discarding work.
The best-practice solution is a PR guard workflow that reads the MANAGED_FILES manifest and fails the PR immediately if any of those files are touched. The contributor is redirected to the design-docs repo to make the change there.
protect-design-docs.yml (deploy to every downstream repo)
# .github/workflows/protect-design-docs.yml
# Fails any PR that modifies a file managed by the central design-docs repository.
# Distribute this workflow via the design-docs sync (add it to MANAGED_FILES).
name: Protect Managed DesignDocs Files
on:
pull_request:
paths:
- '.DesignDocs/**'
jobs:
check-managed-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fail if managed files are modified
run: |
MANIFEST=".DesignDocs/MANAGED_FILES"
if [ ! -f "$MANIFEST" ]; then
echo "No MANAGED_FILES manifest found — skipping check."
exit 0
fi
# Files changed in this PR that are inside .DesignDocs/
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
| grep '^\.DesignDocs/')
VIOLATIONS=""
while IFS= read -r managed_file || [ -n "$managed_file" ]; do
[[ -z "$managed_file" || "$managed_file" == \#* ]] && continue
if echo "$CHANGED" | grep -qF ".DesignDocs/${managed_file}"; then
VIOLATIONS="$VIOLATIONS\n - .DesignDocs/${managed_file}"
fi
done < "$MANIFEST"
if [ -n "$VIOLATIONS" ]; then
echo "❌ This PR modifies files that are owned by the design-docs repository:"
printf "$VIOLATIONS\n"
echo ""
echo "To update these files, open a PR in https://github.com/hummingbot/design-docs."
echo "Changes will be automatically synced to this repo on the next publish cycle."
exit 1
fi
echo "✅ No managed files were modified."Tip: Add
protect-design-docs.ymlitself toMANAGED_FILESso every downstream repo gets the guard workflow automatically on the first sync.
CODEOWNERS (belt-and-suspenders)
Add the following to each downstream repo's CODEOWNERS file to require review from a documentation team even if the guard workflow is somehow bypassed:
# Files managed by the central design-docs repo — reviews must come from the docs team
.DesignDocs/README.md @hummingbot/docs
.DesignDocs/AGENTS.md @hummingbot/docs
.DesignDocs/CODE_OF_CONDUCT.md @hummingbot/docs
.DesignDocs/CONTRIBUTING.md @hummingbot/docs
.DesignDocs/TERMS_OF_USE.md @hummingbot/docs
.DesignDocs/MANAGED_FILES @hummingbot/docs
Combining the CI guard + CODEOWNERS gives two independent layers of protection: the PR fails fast with a clear message, and a human reviewer is required even if the check is bypassed (e.g., by an admin).
GitFlow Publication Style
The workflow above targets develop (not main) in downstream repos — this is intentional and aligned with GitFlow.
Why GitFlow for Document Sync?
GitFlow defines a strict branching model:
main ← production-ready, tagged releases
develop ← integration branch, next release candidate
feature/* ← new features, branched from develop
release/* ← release preparation, branched from develop → merged to main + develop
hotfix/* ← urgent fixes, branched from main → merged to main + develop
chore/* ← maintenance (our sync PRs land here)
By targeting develop, the sync follows the normal review cycle before any change reaches main. This means:
- A human can review the incoming docs update.
- It gets picked up in the next
release/*branch automatically. mainis never written to directly by an automated process — a core GitFlow principle.
Enforcing GitFlow Naming Conventions at the Organization Level
GitHub does not enforce branch naming natively, but the following mechanisms work well together:
1. Branch Protection Rules (per repo)
In Settings → Branches, restrict direct pushes to main and develop and require PRs:
// Example via GitHub API or Terraform
{
"required_pull_request_reviews": { "required_approving_review_count": 1 },
"restrict_pushes": true,
"allowed_push_teams": ["release-managers"]
}2. Required Branch Name Checks via GitHub Actions
Add this check to every repo via a reusable workflow (stored in, e.g., hummingbot/.github):
# .github/workflows/enforce-branch-naming.yml
name: Enforce Branch Naming
on:
pull_request:
branches: [main, develop]
jobs:
check-branch-name:
runs-on: ubuntu-latest
steps:
- name: Validate branch name
run: |
BRANCH="${{ github.head_ref }}"
PATTERN="^(feature|bugfix|release|hotfix|chore|docs|refactor|test)\/[a-z0-9._-]+$"
if [[ ! "$BRANCH" =~ $PATTERN ]]; then
echo "❌ Branch name '$BRANCH' does not follow GitFlow conventions."
echo " Allowed prefixes: feature/, bugfix/, release/, hotfix/, chore/, docs/, refactor/, test/"
exit 1
fi
echo "✅ Branch name '$BRANCH' is valid."3. Repository Rulesets (GitHub Enterprise / Free Org)
GitHub's newer Rulesets (available to all orgs) allow you to define rules at the organization level that apply to all repos automatically — no per-repo configuration needed:
- Organization Settings → Rules → Rulesets → New ruleset
- Target: all repositories (or filtered by topic/name pattern)
- Rules: "Require pull request", "Restrict creations" to pattern
^(feature|bugfix|release|hotfix|chore|docs)\/.*
This is the most powerful enforcement method because it applies before the branch is even created, and cannot be bypassed without explicit admin override.
4. Optional: commitlint + Husky
For commit message enforcement (Conventional Commits style) add to each repo:
pnpm add -D @commitlint/cli @commitlint/config-conventional husky
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'Distribute this config via .DesignDocs/ so every repo stays in sync.
Additional Notes and Benefits
Discoverability for AI Agents
Files like AGENTS.md and CLAUDE.md are increasingly used to give AI coding agents (GitHub Copilot, Cursor, Claude Code, etc.) context about a project. If these files diverge across repos, agents produce inconsistent results. Centralizing them in .DesignDocs/ and syncing automatically means every agent working in any Hummingbot repo gets the same baseline guidance.
Reducing Contributor Friction
The number-one complaint from first-time open source contributors is unclear or inconsistent onboarding. A single canonical CONTRIBUTING.md — always up to date in every repo — directly reduces this friction. Pair it with a CHANGELOG_TEMPLATE.md to standardize release notes format across the ecosystem.
Compliance and Legal Consistency
CODE_OF_CONDUCT.md and TERMS_OF_USE.md are legal documents. Having multiple slightly-different versions across repos is a compliance liability. Centralizing them ensures that any legal updates (e.g., updated contact email, new license clause) propagate everywhere within one PR cycle.
Making the Pattern Reusable Beyond Hummingbot
This paradigm is not unique to Hummingbot. Any GitHub organization with multiple related repos benefits from it. The workflow above is generic — only the topic name (hummingbot-design-docs) and the target branch (develop) need to change for another organization to adopt it.
The pattern could be published as a GitHub Actions reusable workflow or even a GitHub App to make adoption even easier across the open source community.
Versioning and Rollback
Because the sync happens via Pull Requests (not direct pushes), there is a complete audit trail. If a document update causes problems in a downstream repo, rolling back is as simple as reverting the merge commit — the same as any other code change.
Suggested Next Steps
- Create the
hummingbot/design-docsrepository and seed it with the current best-version of each shared document. - Tag the initial set of downstream repos (
gateway,hummingbot-api,dashboard) with thehummingbot-design-docstopic. - Add the
ORG_PUBLISH_PATas an org-level Actions secret. - Deploy the
enforce-branch-naming.ymlworkflow as an org-level reusable workflow so new repos inherit it automatically. - Add a
CODEOWNERSentry in thedesign-docsrepo requiring review from a documentation maintainer team before any merge tomain. - Open a GitHub Discussion in the main
hummingbotorg to gather community feedback on the naming convention (DesignDocsvsSharedDocsvs.hummingbot) before committing to it.
This document is itself a candidate for the .DesignDocs repository — it should be kept in sync across all tagged Hummingbot repos.
Impact
Not hard to set-up, as long as you have global permissions.
Large benefit in keeping key documentation in sync across all repos.
Additional context
No response