Tracked stories (DAG)
Formalize, distribute, and blockingly enforce an org-wide .gitignore minimum baseline — a language-agnostic secrets-only layer that every repo carries verbatim (L1), while each repo freely extends it with its own ecosystem/OS entries (L2).
Current state (verified, not greenfield)
A strong secrets-only baseline already exists at /.gitignore (396 lines) and is referenced by standards/push-protection.md. But it is under-formalized, under-adopted, and weakly enforced:
- Template gap —
repo-template/.gitignore is 15 ad-hoc lines missing *.pem/*.key; new repos start non-compliant even against today's 3-substring check.
- Adoption gap — only 4 of 10 repos carry the real baseline (
bmad-bgreat-suite, TalkTerm, ContentTwin, .github-private); the other 6 have hand-rolled gitignores with no cloud/SSH/tfvars coverage.
- Enforcement gap —
pp_check_gitignore_secrets_block() checks only 3 substrings at warning severity; the documented rule ("MUST start from the baseline") is not actually enforced.
- Distribution gap — detection only; nothing seeds or syncs the baseline (
grep gitignore in deploy-standard-workflows.sh and bootstrap-new-repo.sh = 0).
The DAG
#797 STANDARDIZE ── standalone standards/gitignore-standard.md (L1/L2 model) +
wrap the L1 secrets block in BEGIN/END markers in /.gitignore. [ROOT]
│
├── #798 SEED + DISTRIBUTE ── marker-wrapped block into repo-template +
│ bootstrap-new-repo.sh + deploy sync; shared idempotent
│ upsert_gitignore_baseline() (L2 preserved).
│
└── #799 ENFORCE ── check_gitignore_baseline: block present + unmodified
(hash match), severity ERROR, via the existing
compliance-audit engine. No new workflow. L2 never inspected.
│
#800 BACKFILL + REMEDIATE ── remediation hook + fleet back-fill of the 6
non-baseline repos so the now-blocking check goes green. (after #798 & #799)
Design — the minimum baseline
| Layer |
Content |
Owner |
Enforced |
Extendable |
| L1 secrets baseline |
the 396-line block, wrapped in BEGIN/END petry-projects secrets baseline markers |
central (.github) |
yes — error/blocking, verbatim |
no (drift = finding) |
| L2 ecosystem/OS |
node_modules/, target/, __pycache__/, .DS_Store, … (github/gitignore templates) |
per-repo, below END marker |
no |
yes, freely |
Enforcement engine
Wired through the existing compliance-audit-and-improvement.yml (Job 1 → compliance-audit.sh / lib/push-protection.sh → findings → issues). We extend a check function, not add a workflow. Per decision, L1 lands at error severity (blocking tier).
Epic acceptance criteria
Notes / decisions
Tracked stories (DAG)
Formalize, distribute, and blockingly enforce an org-wide
.gitignoreminimum baseline — a language-agnostic secrets-only layer that every repo carries verbatim (L1), while each repo freely extends it with its own ecosystem/OS entries (L2).Current state (verified, not greenfield)
A strong secrets-only baseline already exists at
/.gitignore(396 lines) and is referenced bystandards/push-protection.md. But it is under-formalized, under-adopted, and weakly enforced:repo-template/.gitignoreis 15 ad-hoc lines missing*.pem/*.key; new repos start non-compliant even against today's 3-substring check.bmad-bgreat-suite,TalkTerm,ContentTwin,.github-private); the other 6 have hand-rolled gitignores with no cloud/SSH/tfvars coverage.pp_check_gitignore_secrets_block()checks only 3 substrings atwarningseverity; the documented rule ("MUST start from the baseline") is not actually enforced.grep gitignoreindeploy-standard-workflows.shandbootstrap-new-repo.sh= 0).The DAG
Design — the minimum baseline
BEGIN/END petry-projects secrets baselinemarkers.github)node_modules/,target/,__pycache__/,.DS_Store, … (github/gitignore templates)Enforcement engine
Wired through the existing
compliance-audit-and-improvement.yml(Job 1 →compliance-audit.sh/lib/push-protection.sh→ findings → issues). We extend a check function, not add a workflow. Per decision, L1 lands aterrorseverity (blocking tier).Epic acceptance criteria
gitignore-standard.mdpublished; L1 block marker-wrapped in/.gitignore; push-protection.md repointed.upsert_gitignore_baseline()unit-tested (L2 preserved).check_gitignore_baseline= error on missing/drifted, pass on present+unmodified; L2 extensions still pass; audit tests green.Notes / decisions
ci.ymlassertion to make it a per-repo required status check (true PR-time gate) — flagged, not required.