Skip to content

Epic A — Workflow & CI Governance (Sprint 1) #9707

Description

@arii

A0) Global Policy (applies to A1–A3)
A0.1 Gemini Usage Reduction Mandate
Use Gemini only in tightly controlled cases:

At PR open (single pass)

One initial review pass only if deterministic gates are green.

At PR merge queue (optional)

Only for high-risk label + changed files hitlist.

No per-commit auto-invocation

Remove “review-on-every-update” behavior unless explicitly labeled.

Hard Constraints
Max Gemini invocations per PR: 1 default, 2 with ai:required label.

If lint/type/knip fails → Gemini must not run.

If only docs/comments changed → Gemini must not run.

If only formatting changes → Gemini must not run.

A0.2 Orchestration Principles
Deterministic checks are the gatekeeper.

AI is an escalation path, not the default path.

All AI runs must include a reason code (e.g., risk_file_touched, security_sensitive_change).

Add telemetry per PR: deterministic_fail, ai_skipped, ai_invoked, tokens_used.

A1) Deterministic Gate Before AI Reasoning
A1.1 Workflow order (strict)
eslint

tsc --noEmit

knip

optional: unit-smoke (fast suite)

Only then: Gemini conditional step

A1.2 Fail-fast behavior
First failing deterministic step stops pipeline branch.

Post a diagnostic PR comment with:

failed command

top 20 errors (truncated)

remediation hints

Mark check as failed; do not invoke Gemini.

A1.3 High-risk trigger list for Gemini
Run Gemini only if changed files match any of:

server.ts

middleware.ts

context/WebSocketContext.tsx (until deprecated)

context/webSocketReducer.ts (until migrated)

hooks/useBluetoothHRM.ts

auth, security, workflow configs (.github/workflows/**)

dependency manifests (package.json, lockfile) if major versions changed

Also run if PR has labels:

ai:required

risk:high

security:review

A1.4 Event reduction strategy
Trigger on:

pull_request opened/reopened/ready_for_review

Do not trigger on:

synchronize (by default)

draft updates

label changes (unless ai:required applied)

Optional manual override:

workflow_dispatch with force_ai=true.

A1.5 Acceptance criteria (expanded)
Lint/type/knip failure blocks AI 100% of the time.

Gemini skipped for low-risk PRs with green deterministic checks.

Gemini runs only when risk trigger or explicit label applies.

Invocation count + reason recorded in summary artifact.

A2) Jules Direct-Action Mode
A2.1 CLI contract for jules_ops.py
Add explicit modes:

--mode audit (default, current behavior)

--mode direct (new tactical mode)

Backward compatibility: --direct maps to --mode direct

direct mode behavior
Skip debt-issue generation workflow entirely.

Create branch + patch + PR draft directly.

Attach deterministic checks output in PR body.

audit mode behavior
Preserve issue-first flow and artifact generation.

A2.2 Safety gates for direct mode
Direct mode allowed only when:

deterministic checks pass, and

PR scope is low/medium risk, and

no restricted paths unless --allow-risk-paths flag is set.

A2.3 Observability
Emit structured run summary:

mode used

issue created? (bool)

PR created? (bool)

skipped reason(s)

A2.4 Acceptance criteria (expanded)
--direct creates PR draft with no debt issue.

--mode audit unchanged.

Mode selection visible in logs + CI summary.

A3) Architectural Boundary Lint Rules
A3.1 Rules to add
no-restricted-imports

block transport libs (ws, socket libs) in component tree.

path boundaries

block utils/logger.server imports from client/components.

optional strict rule

block direct WebSocket construction in component files.

A3.2 Error message standard
Each lint violation must say:

why forbidden

where to move logic (services/, hooks/, context adapter)

link to architecture doc section

A3.3 Acceptance criteria (expanded)
Import boundary violations fail CI.

Developer gets actionable remediation message.

Rule exceptions require inline justification + approval label.

A4) Workflow Cleanup: Remove or Fix Broken Pipelines
A4.1 Inventory and classify all workflows
Create a matrix:

Keep (works + valuable)

Fix (valuable but broken/flaky)

Deprecate (unused or superseded)

Delete (non-functional with no owner/use)

A4.2 Mandatory actions
Remove duplicate AI workflows.

Consolidate to one orchestration entrypoint.

Fix Jules workflows to support direct mode path.

Remove dead jobs and unreachable workflow branches.

Enforce owner tags for each workflow file.

A4.3 “No orphan workflow” policy
A workflow must have:

owner

trigger rationale

success metrics

runbook link

Otherwise: mark deprecated and remove in next sprint.

A4.4 Acceptance criteria
Every workflow has owner + purpose.

Broken workflows either fixed or removed within sprint.

CI runtime reduced (target: 20–40% faster for standard PRs).

AI-triggered runs reduced significantly (target: 60–90% fewer).

A5) Suggested GitHub Actions design (reference)
Stage 1 — pr-quality-fast
lint/type/knip

upload diagnostics

fail fast

Stage 2 — ai-gate-decision
evaluate changed files + labels + deterministic results

output run_ai=true/false, reason

Stage 3 — gemini-review (conditional)
only if run_ai=true

single invocation unless ai:required

Stage 4 — workflow-health
verifies no deprecated/broken workflow references

A6) Rollout plan (1 sprint)
Days 1–2
Implement deterministic gate + fail-fast diagnostics.

Add changed-file/label risk gate.

Days 3–4
Implement Jules --mode direct.

Add compatibility + logs.

Day 5
Add boundary lint rules + clear messages.

Fix/remove obsolete workflows and publish workflow ownership matrix.

A7) Definition of Done for Epic A
Epic A is done when:

Deterministic checks always precede any AI.

Gemini invocations are rare, justified, and capped.

Jules direct mode works and audit mode remains intact.

Boundary lint rules block forbidden imports.

Broken/unused workflows are removed or repaired.

CI is faster, cleaner, and cheaper in token/runtime usage.

  1. GitHub Actions: ai-gate-decision (exact pattern)
    Use this as a reusable job inside your PR workflow (or as its own workflow).

name: pr-quality

on:
pull_request:
types: [opened, reopened, ready_for_review, synchronize, labeled]
workflow_dispatch:
inputs:
force_ai:
description: "Force Gemini review"
required: false
default: "false"

permissions:
contents: read
pull-requests: write

jobs:
deterministic:
name: Deterministic checks
runs-on: ubuntu-latest
outputs:
passed: ${{ steps.set_result.outputs.passed }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

  - uses: pnpm/action-setup@v4
  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: "pnpm"

  - run: pnpm install --frozen-lockfile

  - name: ESLint
    id: eslint
    run: pnpm lint

  - name: Typecheck
    id: tsc
    run: pnpm tsc --noEmit

  - name: Knip
    id: knip
    run: pnpm knip

  - name: Set deterministic result
    id: set_result
    if: ${{ success() }}
    run: echo "passed=true" >> "$GITHUB_OUTPUT"

  - name: Set deterministic result (failure)
    if: ${{ failure() }}
    run: echo "passed=false" >> "$GITHUB_OUTPUT"

ai_gate_decision:
name: AI gate decision
runs-on: ubuntu-latest
needs: deterministic
outputs:
run_ai: ${{ steps.decide.outputs.run_ai }}
reason: ${{ steps.decide.outputs.reason }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

  - name: Gather changed files
    id: changed
    uses: tj-actions/changed-files@v45
    with:
      files_yaml: |
        risk:
          - server.ts
          - middleware.ts
          - context/WebSocketContext.tsx
          - context/webSocketReducer.ts
          - hooks/useBluetoothHRM.ts
          - .github/workflows/**
          - package.json
          - pnpm-lock.yaml
        docs_only:
          - "**/*.md"
          - "**/*.mdx"
          - "docs/**"

  - name: Decide whether to run AI
    id: decide
    env:
      DETERMINISTIC_PASSED: ${{ needs.deterministic.outputs.passed }}
      FORCE_AI: ${{ inputs.force_ai || 'false' }}
      PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
      ANY_CHANGED: ${{ steps.changed.outputs.any_changed }}
      RISK_CHANGED: ${{ steps.changed.outputs.risk_any_changed }}
      DOCS_ONLY: ${{ steps.changed.outputs.only_changed == 'true' && steps.changed.outputs.docs_only_any_changed == 'true' }}
      EVENT_NAME: ${{ github.event_name }}
      EVENT_ACTION: ${{ github.event.action }}
    shell: bash
    run: |
      run_ai=false
      reason=""

      # Hard stop: deterministic fail
      if [[ "$DETERMINISTIC_PASSED" != "true" ]]; then
        echo "run_ai=false" >> "$GITHUB_OUTPUT"
        echo "reason=deterministic_failed" >> "$GITHUB_OUTPUT"
        exit 0
      fi

      # Force override
      if [[ "$FORCE_AI" == "true" ]]; then
        echo "run_ai=true" >> "$GITHUB_OUTPUT"
        echo "reason=manual_force" >> "$GITHUB_OUTPUT"
        exit 0
      fi

      # Docs-only skip
      if [[ "$DOCS_ONLY" == "true" ]]; then
        echo "run_ai=false" >> "$GITHUB_OUTPUT"
        echo "reason=docs_only" >> "$GITHUB_OUTPUT"
        exit 0
      fi

      # Label-driven allowlist
      if echo "$PR_LABELS" | grep -Eiq '"ai:required"|"risk:high"|"security:review"'; then
        echo "run_ai=true" >> "$GITHUB_OUTPUT"
        echo "reason=label_trigger" >> "$GITHUB_OUTPUT"
        exit 0
      fi

      # Risk file trigger
      if [[ "$RISK_CHANGED" == "true" ]]; then
        echo "run_ai=true" >> "$GITHUB_OUTPUT"
        echo "reason=risk_file_changed" >> "$GITHUB_OUTPUT"
        exit 0
      fi

      # Aggressive reduction default
      echo "run_ai=false" >> "$GITHUB_OUTPUT"
      echo "reason=low_risk_skip" >> "$GITHUB_OUTPUT"

  - name: Post gate summary
    uses: actions/github-script@v7
    with:
      script: |
        const runAi = "${{ steps.decide.outputs.run_ai }}";
        const reason = "${{ steps.decide.outputs.reason }}";
        const body = `### AI Gate Decision\n- run_ai: **${runAi}**\n- reason: \`${reason}\``;
        github.rest.issues.createComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: context.issue.number,
          body
        });

gemini_review:
name: Gemini review (conditional)
needs: [deterministic, ai_gate_decision]
if: ${{ needs.ai_gate_decision.outputs.run_ai == 'true' }}
runs-on: ubuntu-latest
steps:
- run: echo "Invoke Gemini once here. reason=${{ needs.ai_gate_decision.outputs.reason }}"
# add your actual Gemini invocation action/script
2) PR Template Checklist (drop-in)
Create/update .github/pull_request_template.md:

Summary

Risk & Scope

  • Low risk
  • Medium risk
  • High risk (requires ai:required or risk:high label)

Architecture Compliance

  • No transport logic added in React components
  • No server-only modules imported into client code
  • Service/store boundaries preserved (service -> store -> hooks -> components)

Service/Store Impact

  • Touches service layer (list files):
  • Touches state store/reducer (list files):
  • Migration/backward compatibility considered

Auth/Security

  • Affects auth/session/token flow
  • WebSocket upgrade/auth assumptions reviewed
  • No secrets introduced in code/config/logs

Testing

  • Lint/type/knip pass locally
  • Unit tests updated
  • Integration tests updated
  • VRT/E2E impact assessed

Accessibility

  • Realtime announcements use correct aria-live strategy
  • No high-frequency screen-reader spam introduced
  • Contrast/accessibility checks considered
  1. Workflow Ownership Metadata Checklist (for each workflow file)
    Add as front-matter comment at top of each workflow (or maintain in a separate docs/workflow-owners.md table):

owner: @team-devex

purpose: Deterministic PR quality gate and conditional AI review

triggers: pull_request(opened,reopened,ready_for_review,synchronize,labeled)

sla: P1 failures acknowledged within 1 business day

deprecation_date: n/a

runbook: docs/runbooks/pr-quality.md

metrics:

- median_duration

- failure_rate

- ai_invocation_rate

- deterministic_fail_rate

Review checklist for cleanup

Has owner

Has purpose

Trigger still needed

Not duplicated by another workflow

Has runbook

Has success metrics

Passes dry-run test

If not, mark deprecated and remove this sprint

  1. Jules CLI mode contract (quick spec)
    jules_ops.py --mode audit # default, create debt issue + follow audit flow
    jules_ops.py --mode direct # skip issue creation, open tactical PR draft
    jules_ops.py --direct # alias for --mode direct
    Guardrails:

Direct mode blocked on deterministic failure.

Direct mode blocked for high-risk files unless --allow-risk-paths.

If you want, next I can provide:

a minimal jules_ops.py argparse patch, and

a single consolidated pr-quality.yml that wires deterministic → decision → conditional Gemini end-to-end.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status
    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions