Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/unreleased/104-docs-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **repo-required-gate gains an opt-in `docs-system` docs gate.** New boolean input (default false, same opt-in pattern as `stack`): when true, pull_request runs add a `docs-gate` job (`npm run docs:render -- --check` + `node scripts/doc-health/health.mjs --repo . --changed-from origin/<base> --json`) aggregated by `decision` with skipped != failed for non-opted consumers. Its package-manager detection is lockfile-aware — `npm ci` only when a `package-lock.json` exists, else `npm install --no-package-lock` — mirroring `node-ci.yml`, so a docs consumer that ships `package.json` + docs scripts without a committed lockfile isn't failed before either check runs. The `examples/changelog-fragment.yml` and `examples/repo-update-log-fragment.yml` caller stubs are retired (guidance cleanup only — the reusable workflow bodies stay). (#104)
92 changes: 92 additions & 0 deletions .github/workflows/repo-required-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,20 @@ on:
description: "Fail if go-generate-command leaves a dirty tree (stale committed generated code)."
type: boolean
default: false
# ---- Docs-system gate (#104) -----------------------------------------
docs-system:
description: |
Opt-in docs gate for repo-template docs-system consumers (same
opt-in pattern as `stack`). When true, a docs-gate job runs on
pull_request events after detect, in the consumer checkout:
`npm run docs:render -- --check` (fails on stale committed rendered
blocks) and `node scripts/doc-health/health.mjs --repo .
--changed-from origin/<base> --json` (fails on blocking findings).
Requires the repo-template docs-system scripts. Consumers that
leave this false are unaffected: the job is skipped and the
decision job does not require it (skipped != failed).
type: boolean
default: false
workflow-library-ref:
description: |
Git ref (tag, branch, or SHA) of ArchonVII/github-workflows used
Expand Down Expand Up @@ -230,6 +244,7 @@ jobs:
run-policy-validation: ${{ steps.detect.outputs.run-policy-validation }}
snapshot-only: ${{ steps.detect.outputs.snapshot-only }}
run-snapshot-validation: ${{ steps.detect.outputs.run-snapshot-validation }}
run-docs-gate: ${{ steps.detect.outputs.run-docs-gate }}
forced-full: ${{ steps.detect.outputs.forced-full }}
files-summary: ${{ steps.detect.outputs.files-summary }}
steps:
Expand All @@ -256,6 +271,7 @@ jobs:
SNAPSHOT_PATHS: ${{ inputs.snapshot-paths }}
SNAPSHOT_TEST_COMMAND: ${{ inputs.snapshot-test-command }}
FORCE_FULL_CI_LABEL: ${{ inputs.force-full-ci-label }}
DOCS_SYSTEM: ${{ inputs.docs-system }}
WORKFLOW_LIBRARY_REF: ${{ inputs.workflow-library-ref }}
with:
script: |
Expand Down Expand Up @@ -303,6 +319,7 @@ jobs:
snapshotPaths: process.env.SNAPSHOT_PATHS,
snapshotTestCommand: process.env.SNAPSHOT_TEST_COMMAND,
forceFullCiLabel: process.env.FORCE_FULL_CI_LABEL,
docsSystem: process.env.DOCS_SYSTEM === 'true',
isPullRequest,
});

Expand All @@ -319,6 +336,7 @@ jobs:
core.setOutput('run-policy-validation', String(result.outputs.runPolicyValidation));
core.setOutput('snapshot-only', String(result.outputs.snapshotOnly));
core.setOutput('run-snapshot-validation', String(result.outputs.runSnapshotValidation));
core.setOutput('run-docs-gate', String(result.outputs.runDocsGate));
core.setOutput('forced-full', String(result.outputs.forcedFull));
core.setOutput('files-summary', result.filesSummary);

Expand Down Expand Up @@ -717,6 +735,69 @@ jobs:
shell: bash
run: ${{ inputs.snapshot-test-command }}

# Docs-system gate (#104): opt-in via the docs-system input, PR events only.
# Runs in the CONSUMER checkout on every lane — doc drift is precisely a
# docs-only concern, so the job is deliberately NOT path-routed. Conditioned
# on the input directly rather than on `needs.detect.outputs.run-docs-gate`:
# a caller whose workflow-library-ref lags this workflow body would import a
# classifier without that output, and an empty-string comparison would
# silently no-op the gate for an opted-in consumer. The detect output still
# exists so the lane summary reports docs-gate as required/skipped honestly.
docs-gate:
name: docs gate
needs:
- detect
if: always() && inputs.docs-system && github.event_name == 'pull_request' && needs.detect.outputs.ok == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# health.mjs --changed-from resolves the merge base with the PR base
# branch; the default depth-1 checkout cannot reach it, so fetch full
# history (also brings in origin/<base>).
- uses: actions/checkout@v7
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version: ${{ fromJSON(inputs.node-versions)[0] }}

- name: Detect package manager
id: pm
shell: bash
run: |
if [ -f pnpm-lock.yaml ]; then
npm install -g pnpm
echo "install=pnpm install --frozen-lockfile" >> "$GITHUB_OUTPUT"
elif [ -f yarn.lock ]; then
echo "install=yarn install --frozen-lockfile" >> "$GITHUB_OUTPUT"
elif [ -f package-lock.json ]; then
echo "install=npm ci" >> "$GITHUB_OUTPUT"
Comment thread
ArchonVII marked this conversation as resolved.
else
# No committed lockfile: a docs-system consumer may ship
# package.json + docs scripts without one. `npm ci` REQUIRES a
# lockfile and would fail the docs gate before either check runs,
# so fall back to a non-lockfile install — mirrors node-ci.yml's
# "Detect package manager" step (node-ci.yml, no-lockfile branch).
# --no-package-lock keeps CI from fabricating a stray lockfile.
echo "install=npm install --no-package-lock" >> "$GITHUB_OUTPUT"
fi

- name: Install
shell: bash
run: ${{ steps.pm.outputs.install }}

- name: Rendered docs drift check
shell: bash
run: npm run docs:render -- --check

- name: Doc health blocking findings
shell: bash
env:
BASE_REF: ${{ github.base_ref }}
run: node scripts/doc-health/health.mjs --repo . --changed-from "origin/${BASE_REF}" --json

decision:
name: decision
needs:
Expand All @@ -729,6 +810,7 @@ jobs:
- python-ci
- go-ci
- snapshot-validation
- docs-gate
if: always()
runs-on: ubuntu-latest
steps:
Expand All @@ -744,6 +826,7 @@ jobs:
RUN_WORKFLOW_VALIDATION: ${{ inputs.run-workflow-validation && needs.detect.outputs.run-workflow-validation == 'true' }}
RUN_POLICY_VALIDATION: ${{ inputs.run-policy-validation && needs.detect.outputs.run-policy-validation == 'true' }}
RUN_SNAPSHOT_VALIDATION: ${{ needs.detect.outputs.run-snapshot-validation }}
RUN_DOCS_GATE: ${{ inputs.docs-system }}
DETECT_RESULT: ${{ needs.detect.result }}
CONTRACT_RESULT: ${{ needs.pr-contract.result }}
WORKFLOW_RESULT: ${{ needs.workflow-validation.result }}
Expand All @@ -753,6 +836,7 @@ jobs:
PYTHON_RESULT: ${{ needs.python-ci.result }}
GO_RESULT: ${{ needs.go-ci.result }}
SNAPSHOT_RESULT: ${{ needs.snapshot-validation.result }}
DOCS_GATE_RESULT: ${{ needs.docs-gate.result }}
EVENT_NAME: ${{ github.event_name }}
RUN_PR_CONTRACT: ${{ inputs.run-pr-contract }}
run: |
Expand Down Expand Up @@ -807,6 +891,14 @@ jobs:
require_success "snapshot validation" "$SNAPSHOT_RESULT"
fi

# Docs gate is PR-only (mirrors pr-contract): the job itself skips
# on push/merge_group even for opted-in consumers, so the event
# guard here is load-bearing — without it every non-PR run of an
# opted-in consumer would fail on a skipped-but-required job.
if [ "$RUN_DOCS_GATE" = "true" ] && [ "$EVENT_NAME" = "pull_request" ]; then
require_success "docs gate" "$DOCS_GATE_RESULT"
fi

if [ "$failed" -ne 0 ]; then
exit 1
fi
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ provider self-test workflows.

| Workflow | Purpose | Caller |
| --- | --- | --- |
| [`changelog-fragment.yml`](.github/workflows/changelog-fragment.yml) | Requires an added `.changelog/unreleased/*.md` fragment when configured source paths change, with a label-based opt-out. | [`examples/changelog-fragment.yml`](examples/changelog-fragment.yml) |
| [`repo-update-log-fragment.yml`](.github/workflows/repo-update-log-fragment.yml) | Requires an added `docs/repo-update-log/*.md` fragment for code/config/behavior/protected-doc/workflow/policy PRs, with a body-recorded skip only for unprotected doc-only fixes. | [`examples/repo-update-log-fragment.yml`](examples/repo-update-log-fragment.yml) |
| [`changelog-fragment.yml`](.github/workflows/changelog-fragment.yml) | Requires an added `.changelog/unreleased/*.md` fragment when configured source paths change, with a label-based opt-out. | Example retired (#104); existing installed callers keep working. |
| [`repo-update-log-fragment.yml`](.github/workflows/repo-update-log-fragment.yml) | Requires an added `docs/repo-update-log/*.md` fragment for code/config/behavior/protected-doc/workflow/policy PRs, with a body-recorded skip only for unprotected doc-only fixes. | Example retired (#104); existing installed callers keep working. |
| [`doc-orphan-detector.yml`](.github/workflows/doc-orphan-detector.yml) | Scheduled backstop for committed docs stranded on pushed branches with no open PR. Reports path-only tracking issues and never commits or pushes. | [`examples/doc-orphan-detector.yml`](examples/doc-orphan-detector.yml) |
| [`doc-policy-lint.yml`](.github/workflows/doc-policy-lint.yml) | Warning-only document-policy lint for durable docs: status headers, charter budgets, supersession links, active-doc placeholders, index coherence, and stale active-doc terms. | [`examples/doc-policy-lint.yml`](examples/doc-policy-lint.yml) |
| [`anomaly-triage.yml`](.github/workflows/anomaly-triage.yml) | Reads a per-PR anomalies file, classifies entries as PR-related or unrelated, posts sticky review comments, and can open downstream issues. | [`examples/anomaly-triage.yml`](examples/anomaly-triage.yml) |
Expand Down
9 changes: 9 additions & 0 deletions docs/repo-update-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ This log records agent-visible repository changes that should be easy to audit l
- **Propagation:** none | pending <repo/path> | completed <repo/path>
```

## 2026-07-04 - Docs-gate lane (docs-system input) + fragment example cleanup

- **Issue/PR:** #104 / (pending)
- **Branch:** agent/claude/104-docs-gate
- **Changed paths:** .github/workflows/repo-required-gate.yml, scripts/classify-pr.mjs, scripts/classify-pr.test.mjs, scripts/workflow-structure.test.mjs, examples/repo-required-gate.yml, examples/changelog-fragment.yml (deleted), examples/repo-update-log-fragment.yml (deleted), README.md, .changelog/unreleased/104-docs-gate.md, docs/repo-update-log.md
- **What changed:** Added an opt-in `docs-system` boolean input (default false) to the reusable `repo-required-gate`. When true, a `docs-gate` job runs on pull_request events after `detect` in the consumer checkout (`npm run docs:render -- --check` + `node scripts/doc-health/health.mjs --repo . --changed-from origin/<base> --json`, fetch-depth 0 for the merge base); `decision` requires it only for opted-in consumers on pull_request events (skipped != failed, event guard mirrors pr-contract), and the job conditions on the input directly so a lagging `workflow-library-ref` cannot silently no-op the gate. `classify-pr.mjs` threads `docsSystem` → `run-docs-gate` output + lane-summary jobs lists. **Review round (PR #105 thread):** the docs-gate's "Detect package manager" step fell to `npm ci` with no lockfile guard; `npm ci` REQUIRES a `package-lock.json`, so an opted-in docs consumer that ships `package.json` + docs scripts without a committed lockfile would go red before either docs check ran. Added the same `elif [ -f package-lock.json ]` → `npm ci`, else `npm install --no-package-lock` fallback the shared `node-ci.yml` already uses (node-ci.yml "Detect package manager", no-lockfile branch). Retired the `examples/changelog-fragment.yml` and `examples/repo-update-log-fragment.yml` caller stubs (guidance cleanup only — the reusable workflow bodies stay for existing consumers; registry/baseline migration is deferred to the S3/C1/T1 cleanup). P1 of the docs-system epic (ArchonVII/repo-template#124).
- **Verification:** TDD RED: `npx vitest run scripts/classify-pr.test.mjs scripts/workflow-structure.test.mjs` → 12 failed / 67 passed before implementation. GREEN: same command → 79/79; full `npm test` (vitest 4.1.7) → 9 files / 179 tests / 179 pass on the lane worktree (baseline on `origin/main` was 168; net +11). `actionlint .github/workflows/repo-required-gate.yml examples/repo-required-gate.yml` exited 0 locally (actionlint 1.7.7 binary). `git diff --check` exited 0. Review-round re-verify after the lockfile fallback: `actionlint .github/workflows/repo-required-gate.yml` exit 0, `npx vitest run` → 179/179.
- **Propagation:** pending — `@v1` retag is owner-gated (retag ships the gate fleet-wide); after retag, the repo-template caller flips `docs-system: true` with a live-red scratch-branch proof (ArchonVII/repo-template#124 P1, issue #104 item 3); archon-setup snapshot refresh picks up the retired example stubs.

## 2026-07-01 - Example-caller concurrency + anomaly path filter

- **Issue/PR:** #101 / (pending)
Expand Down
25 changes: 0 additions & 25 deletions examples/changelog-fragment.yml

This file was deleted.

13 changes: 13 additions & 0 deletions examples/repo-required-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ jobs:
# src/snapshots/
# snapshot-test-command: npm run test:snapshots

# ------ Docs-system gate (repo-template docs-system repos) ----------
# Opt-in, PR events only, runs on EVERY lane (doc drift is precisely a
# docs-only concern). When true, a docs-gate job runs after detect:
# npm run docs:render -- --check (stale committed rendered blocks)
# node scripts/doc-health/health.mjs --repo . --changed-from origin/<base> --json
# (blocking doc-health findings)
# Requires the repo-template docs-system scripts (docs:render npm
# script + scripts/doc-health/health.mjs). Leave false/absent
# otherwise: the job is skipped and the decision check does not
# require it.
#
# docs-system: true

# ------ Force-full escape hatch (any stack) -------------------------
# Default label is `ci:full`. Apply it to a PR to bypass path-based
# routing and run the full language CI surface — e.g. a workflow-only
Expand Down
24 changes: 0 additions & 24 deletions examples/repo-update-log-fragment.yml

This file was deleted.

Loading