Skip to content
Draft
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
81 changes: 81 additions & 0 deletions .github/workflows/grc-product.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Central execution only. Consumers retain trigger/concurrency declarations.
# Proposed until linux-cluster-ops#326 and QSR#136/#137 admit this caller.
name: GRC Product

on:
workflow_call:

permissions:
contents: read

jobs:
product:
name: Lint, tests, and coverage
runs-on:
group: CWL CI isolated
labels: [self-hosted, Linux, X64, cwlab-ci-isolated]
timeout-minutes: 20
defaults:
run:
shell: bash --noprofile --norc -e -o pipefail {0}
steps:
- name: Admit GRC caller before checkout
env:
CALLER_REPOSITORY: ${{ github.repository }}
CALLER_EVENT: ${{ github.event_name }}
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
run: |
test "$CALLER_REPOSITORY" = "ContextualWisdomLab/governance-risk-compliance"
case "$CALLER_EVENT" in
pull_request)
test "$HEAD_REPOSITORY" = "$CALLER_REPOSITORY"
;;
push|workflow_dispatch) ;;
*) exit 1 ;;
esac
- name: Harden runner
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
with:
egress-policy: audit
- name: Checkout exact source revision
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- name: Verify exact source head
env:
SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Set up uv without a shared cache
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "0.12.5"
enable-cache: false
cache-local-path: ${{ runner.temp }}/grc-uv-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
- name: Install the hash-locked product
run: uv sync --locked --extra dev
- name: Lint
run: uv run ruff check cwl_grc tests
- name: Docstrings
run: uv run interrogate --fail-under 100 cwl_grc
- name: Tests and coverage
run: >-
uv run pytest --cov=cwl_grc --cov-branch
--cov-report=term-missing --cov-fail-under=100
- name: Compile
run: uv run python -m compileall -q cwl_grc tests
- name: Verify lock freshness
run: uv lock --check
- name: Verify clean product tree
run: |
git diff --check
test -z "$(git status --porcelain)"
- name: Remove job-private uv cache
if: always()
env:
UV_CACHE_DIR: ${{ runner.temp }}/grc-uv-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
run: rm -rf -- "$UV_CACHE_DIR"
10 changes: 10 additions & 0 deletions CHANGELOG.d/20261009-grc-central-quality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### GRC central quality workflow remains staged behind isolated runner admission

- Add the central `workflow_call` quality lane, thin-caller template, and
regression contracts for GRC lint/tests/coverage while keeping product policy
in `ContextualWisdomLab/governance-risk-compliance`.
- This is source preparation only. No consumer caller is installed, no runner
access is expanded, and no hosted fallback is introduced. Activation still
requires the authorized disposable isolated-runner and cleanup canary,
exact-head required Checks, independent approval, and ordinary protected
integration.
115 changes: 115 additions & 0 deletions docs/doctoring/grc-product-central-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# GRC Product central execution — staged rollout

Status: **Implemented source / cloud policy-tested / not activated**.

## Ownership and existing delivery

The GRC policy/control/evidence kernel remains in
`ContextualWisdomLab/governance-risk-compliance`. This control plane owns only
`.github/workflows/grc-product.yml`, its policy tests, `.github/actionlint.yaml`
(custom label validation), and the thin-caller template.
There is no new identity, risk, audit, scanner, database, or officer UI body here.

- `ContextualWisdomLab/.github#2565` already owns conversion of all existing
central workflows to self-hosted. Do not duplicate its 73-job change.
- `ContextualWisdomLab/linux-cluster-ops#326` owns S1 placement, registration,
access custody, image provisioning, reset and actual canary.
- `ContextualWisdomLab/quarantine-sandbox-runtime#136` and
`ContextualWisdomLab/quarantine-sandbox-runtime#137` own reachability-attestation
contracts. Limited point probes are not complete CIDR or clean-job evidence.
- GRC privacy #69/#70; Keyverse #38, #55–#58, revision #73 and coverage #74;
PostgreSQL #18; version-one API #53; officer workspace #34/#54; readiness #17;
domain roadmap #27–#30; and baseline refresh #75 retain their existing owners.
All those references are in `ContextualWisdomLab/governance-risk-compliance`.

A useful commercial GRC must distinguish artifact presence from tested control
operating effectiveness. The existing roadmap's dependency sequence remains:
identity/tenancy, PostgreSQL, evidence recovery, operations and stable API;
then internal controls, obligations/catalog, risk/audit and connected officer
workflows. This CI increment does not close any of those product issues.

## Central contract

The workflow accepts no executable caller input or secret inheritance. It runs
only the fixed GRC repository on same-repository PRs, pushes and manual runs.
Foreign callers, external PR heads, missing PR repository identity and unsupported
events fail in an actual pre-checkout shell; they are not skipped into success.
This guard executes after runner assignment and is not pre-lease isolation proof.

Only `CWL CI isolated` AND `[self-hosted, Linux, X64, cwlab-ci-isolated]` may run
the product job. No hosted fallback, control/scanner pool or existing runner
relabeling is introduced. The original exact-head checkout, immutable Actions,
`contents: read`, no persisted Git credentials, Python 3.12, locked uv install,
Ruff, docstrings 100%, production statement/branch coverage 100%, compile, lock
freshness and dirty-tree gates remain. UV cache is job-private, not shared or
uploaded, and cleanup runs even on failure. VM cancellation/termination reset,
HOME/tool-cache/workspace cleanup and network isolation remain operator gates.

StepSecurity harden-runner is retained, not silently removed. Its self-hosted
agent and subscription/image acceptance must be established by the operator;
a successful/no-op action is not monitoring or isolation evidence. No subscription
purchase is authorized here. References: GitHub's reusable-workflow runner context
reference and StepSecurity's self-hosted/how-it-works documentation.

## Consumer rollout, without dangling remote pins

`docs/templates/grc-product.yml.in` preserves GRC trigger/path exclusions and
workflow-scope PR-only cancellation. ARCHITECTURE.md is deliberately not ignored.
Do not add the same concurrency group to the reusable workflow: it can cancel
its calling run. Changing to a reusable job may change check-context display
names; inspect actual contexts and reconcile through the existing protection owner,
never remove required gates as a shortcut.

1. Independently review and publish this central source through the ordinary
protected path. This session does not authorize commit, push or merge.
2. Obtain a real accepted isolated group, caller repository/workflow/ref/event/fork
eligibility, disposable image and cleanup proof under ops #326 and QSR.
Runner availability and central workflow availability are separate gates.
3. Replace `__CENTRAL_PRODUCT_COMMIT__` in the template with the exact reviewed,
published 40-hex central commit. Verify that commit contains this workflow.
Copy only the rendered caller to GRC `.github/workflows/product.yml` in an
owner-reviewed PR; do not use a fabricated hash, mutable ref or unresolved pin.
4. Update GRC's dirty-tree workflow regression to validate the released central
contract rather than require execution commands in the caller. Keep its
concurrency regression and all product quality thresholds unchanged.
5. Have existing #18, #34 and #17 owners inventory and migrate PostgreSQL, UI
and readiness executable jobs using independently reviewed central contracts.
Those contracts are not implemented by this initial Product slice.
6. Inspect GitHub-managed dynamic CodeQL/Code Quality configuration through its
current owner. Moving YAML cannot move those platform-managed jobs.
7. On the unchanged caller/central source pair, record actual run/job ID, runner
name/group, tool-image identity, all executed checks, cleanup and qualifying
independent approval. Only that evidence permits a runtime-complete claim.

## Observed boundary — October 5, 2026

The initial GRC branch is `529cf321f134e26c0cd379ee53c06ab5297363b6`.
Central baseline is `37b10243cec3d160ecc9c1be75c71428b160a703`.
The GRC repository runner inventory returned zero. The original GRC worktree is
unchanged. Central preparation is in a dedicated cloud clone on S1, not another
writer's checkout. The operator infrastructure and production containers were
not modified. No remote central approval, protected integration, self-hosted
GitHub Product run, full-organizational migration or release is claimed.

## Execution evidence

New central-contract existence test first failed with
`GRC Product execution must be centrally owned`. The implemented workflow then
passed 8 policy tests normally and 8 with `GITHUB_ACTIONS=true` on S1. The
admission regression executes the actual shell for allowed and rejected tuples.
This is cloud source validation, not registered runner execution.

Additional actual S1 acceptance: actionlint 1.7.12 (official archive digest verified),
new policy-test Ruff E9/F/I and `git diff --check` pass. The unchanged GRC baseline
ran in a resource-bounded, non-root, read-only Python 3.12.15 container with no
host socket, service mounts or credentials. Locked uv 0.12.5 installation,
Ruff, docstrings, compile and lock freshness pass; 48 tests cover all 769
production statements and 152 branches. The run emitted 36 existing dependency
deprecation warnings (Starlette HTTPX and SQLite datetime adapter), which remain
visible rather than being suppressed. The product tree remains clean and the
container was removed. The bootstrap uv package was fetched by exact version,
not a hash-locked bootstrap; this receipt is not runner-image supply-chain approval.
Full central repository tests, actual GitHub Actions, protected integration and
release remain NOT_RUN. Further gate
results must be reported from their actual command output, not inherited from
central #2565 or any product feature PR.
7 changes: 7 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Product and Technical Gap Baseline

## 2026-10-09 GRC central reusable quality lane

| Gap | Exact evidence | Action | Status |
|---|---|---|---|
| CONTROL-GRC-CENTRAL-QUALITY-01 | `.github#2602` predecessor `efb53f7b44d09614993702e4a20d75c06a190a37` carries the GRC reusable workflow, thin-caller template, and contract tests. Canonical runner-policy owner `.github#2565` is integrated at `ef65b2fdc2036ccaca8d19c1db88dc8075ae9164`; the stack repair preserves both source histories and reapplies the current-time CodeQL fixture repair to the owner tree. | Keep the product caller uninstalled until `linux-cluster-ops#326` provisions and proves disposable isolated capacity and cleanup. Re-fetch the published head and require exact-head required Checks, independent approval, and ordinary protected integration. No hosted fallback, borrowed privileged runner, or bypass. | **Draft / Proposed / merge HOLD** — source staged; runtime activation and protected integration pending. |


## 2026-10-09 reusable R matrix conservation

| Gap | Exact evidence | Action | Status |
Expand Down
33 changes: 33 additions & 0 deletions docs/templates/grc-product.yml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Render __CENTRAL_PRODUCT_COMMIT__ to the reviewed published 40-hex commit.
# Do not activate before the dedicated runner/access/isolation canary passes.
name: Product

on:
push:
branches:
- develop
paths-ignore:
- "docs/**"
- "AGENTS.md"
- "CHANGELOG.md"
- "CLAUDE.md"
- "README.md"
pull_request:
paths-ignore:
- "docs/**"
- "AGENTS.md"
- "CHANGELOG.md"
- "CLAUDE.md"
- "README.md"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
product:
uses: ContextualWisdomLab/.github/.github/workflows/grc-product.yml@__CENTRAL_PRODUCT_COMMIT__
25 changes: 21 additions & 4 deletions tests/test_audit_org_codeql_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ def default_setup_scanning_nothing(name: str) -> dict:
}


def covered_by_recent_analysis(name: str, *, days_ago: int = 1) -> dict:
def covered_by_recent_analysis(
name: str, *, days_ago: int = 1, reference_time: datetime = NOW
) -> dict:
"""Return a repository payload covered by a recent, successful CodeQL analysis."""
created_at = (NOW - timedelta(days=days_ago)).isoformat().replace("+00:00", "Z")
created_at = (reference_time - timedelta(days=days_ago)).isoformat().replace(
"+00:00", "Z"
)
return {
"name": name,
"archived": False,
Expand Down Expand Up @@ -270,7 +274,13 @@ def now(cls, tz=None):
return NOW

monkeypatch.setattr(audit, "datetime", FixedClock)
repositories = [covered_by_recent_analysis("DefaultNowRepo", days_ago=0)]
repositories = [
covered_by_recent_analysis(
"DefaultNowRepo",
days_ago=0,
reference_time=datetime.now(timezone.utc),
)
]

assert audit.audit_codeql_coverage(repositories) == []

Expand Down Expand Up @@ -316,7 +326,14 @@ def now(cls, tz=None):
monkeypatch.setattr(audit, "datetime", FixedClock)
payload_path = tmp_path / "repositories.json"
payload_path.write_text(
json.dumps([covered_by_default_setup("ELUNVERA"), covered_by_recent_analysis("Orgmetra")]),
json.dumps(
[
covered_by_default_setup("ELUNVERA"),
covered_by_recent_analysis(
"Orgmetra", reference_time=datetime.now(timezone.utc)
),
]
),
encoding="utf-8",
)

Expand Down
Loading
Loading