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
140 changes: 90 additions & 50 deletions .github/workflows/pr-orchestrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
fi
fi

- name: Run contract-first tests (no coverage)
- name: Run full test suite (smart-test-full)
if: needs.changes.outputs.skip_tests_dev_to_main != 'true'
shell: bash
env:
Expand All @@ -145,40 +145,23 @@ jobs:
SMART_TEST_TIMEOUT_SECONDS: "1800"
PYTEST_ADDOPTS: "-r fEw"
run: |
echo "πŸ§ͺ Running contract-first test suite (3.12)..."
echo "πŸ§ͺ Running full test suite (smart-test-full, Python 3.12)..."
echo "ℹ️ HATCH_TEST_ENV=${HATCH_TEST_ENV}"
run_layer() {
local label="$1"
shift
local start_ts
start_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "▢️ [${start_ts}] Starting ${label}"
echo " Command: $*"
if "$@"; then
local end_ts
end_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "βœ… [${end_ts}] ${label} completed"
else
local end_ts
end_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "⚠️ [${end_ts}] ${label} incomplete"
fi
}

run_layer "Contract validation" hatch run contract-test-contracts
run_layer "Contract exploration" hatch run contract-test-exploration
run_layer "Scenario tests" hatch run contract-test-scenarios
run_layer "E2E tests" hatch run contract-test-e2e
hatch run smart-test-full

- name: Run unit tests with coverage (3.12)
- name: Generate coverage XML for quality gates
if: needs.changes.outputs.skip_tests_dev_to_main != 'true' && env.RUN_UNIT_COVERAGE == 'true'
env:
PYTEST_ADDOPTS: "-r fEw"
run: |
echo "πŸ§ͺ Running unit tests with coverage (3.12)..."
hatch -e hatch-test.py3.12 run run-cov
hatch -e hatch-test.py3.12 run xml

- name: Upload test logs
if: needs.changes.outputs.skip_tests_dev_to_main != 'true'
uses: actions/upload-artifact@v4
with:
name: test-logs
path: logs/tests/
if-no-files-found: ignore

- name: Upload coverage artifacts
if: needs.changes.outputs.skip_tests_dev_to_main != 'true' && env.RUN_UNIT_COVERAGE == 'true'
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -220,10 +203,19 @@ jobs:
- name: Run Python 3.11 compatibility tests (hatch-test matrix env)
run: |
echo "πŸ” Python 3.11 compatibility checks"
# Run a subset of tests to verify Python 3.11 compatibility
# Focus on unit tests and integration tests (skip slow E2E tests)
hatch -e hatch-test.py3.11 test -- -r fEw tests/unit tests/integration || echo "⚠️ Some tests failed (advisory)"
hatch -e hatch-test.py3.11 run xml || true
mkdir -p logs/compat-py311
COMPAT_LOG="logs/compat-py311/compat_$(date -u +%Y%m%d_%H%M%S).log"
{
hatch -e hatch-test.py3.11 run run -- -r fEw tests/unit tests/integration || echo "⚠️ Some tests failed (advisory)"
hatch -e hatch-test.py3.11 run xml || true
} 2>&1 | tee "$COMPAT_LOG"
- name: Upload compat-py311 logs
if: always()
uses: actions/upload-artifact@v4
with:
name: compat-py311-logs
path: logs/compat-py311/
if-no-files-found: ignore

contract-first-ci:
name: Contract-First CI
Expand Down Expand Up @@ -255,10 +247,29 @@ jobs:
restore-keys: |
${{ runner.os }}-hatch-contract-first-py312-
${{ runner.os }}-hatch-
- name: Prepare repro log directory
run: mkdir -p logs/repro
- name: Run contract validation and exploration
id: repro
run: |
echo "πŸ” Validating runtime contracts..."
echo "Running specfact repro with required CrossHair..." && hatch run specfact repro --verbose --crosshair-required --budget 120 || echo "SpecFact repro found issues"
REPRO_LOG="logs/repro/repro_$(date -u +%Y%m%d_%H%M%S).log"
echo "Running specfact repro with required CrossHair... (log: $REPRO_LOG)"
hatch run specfact repro --verbose --crosshair-required --budget 120 2>&1 | tee "$REPRO_LOG" || echo "SpecFact repro found issues"
- name: Upload repro logs
if: always()
uses: actions/upload-artifact@v4
with:
name: repro-logs
path: logs/repro/
if-no-files-found: ignore
- name: Upload repro reports
if: always()
uses: actions/upload-artifact@v4
with:
name: repro-reports
path: .specfact/reports/enforcement/
if-no-files-found: ignore

cli-validation:
name: CLI Command Validation
Expand Down Expand Up @@ -313,19 +324,30 @@ jobs:
echo "βœ… Found coverage.xml"
- name: Run quality gates (advisory)
run: |
echo "πŸ” Checking coverage (advisory only)..."
COVERAGE_PERCENT_XML=$(grep -o "line-rate=\"[0-9.]*\"" logs/tests/coverage/coverage.xml | head -1 | sed 's/line-rate=\"//' | sed 's/\"//')
if [ -n "$COVERAGE_PERCENT_XML" ] && [ "$COVERAGE_PERCENT_XML" != "0" ]; then
COVERAGE_PERCENT_INT=$(echo "$COVERAGE_PERCENT_XML * 100" | bc -l | cut -d. -f1)
else
COVERAGE_PERCENT_INT=0
fi
echo "πŸ“Š Line coverage (advisory): ${COVERAGE_PERCENT_INT}%"
if [ "$COVERAGE_PERCENT_INT" -lt 30 ]; then
echo "⚠️ Advisory: coverage below 30% β€” permitted under contract-first; prioritize contract/scenario gaps."
else
echo "βœ… Advisory: coverage acceptable"
fi
mkdir -p logs/quality-gates
QG_LOG="logs/quality-gates/quality-gates_$(date -u +%Y%m%d_%H%M%S).log"
{
echo "πŸ” Checking coverage (advisory only)..."
COVERAGE_PERCENT_XML=$(grep -o "line-rate=\"[0-9.]*\"" logs/tests/coverage/coverage.xml | head -1 | sed 's/line-rate=\"//' | sed 's/\"//')
if [ -n "$COVERAGE_PERCENT_XML" ] && [ "$COVERAGE_PERCENT_XML" != "0" ]; then
COVERAGE_PERCENT_INT=$(echo "$COVERAGE_PERCENT_XML * 100" | bc -l | cut -d. -f1)
else
COVERAGE_PERCENT_INT=0
fi
echo "πŸ“Š Line coverage (advisory): ${COVERAGE_PERCENT_INT}%"
if [ "$COVERAGE_PERCENT_INT" -lt 30 ]; then
echo "⚠️ Advisory: coverage below 30% β€” permitted under contract-first; prioritize contract/scenario gaps."
else
echo "βœ… Advisory: coverage acceptable"
fi
} 2>&1 | tee "$QG_LOG"
- name: Upload quality-gates logs
if: always()
uses: actions/upload-artifact@v4
with:
name: quality-gates-logs
path: logs/quality-gates/
if-no-files-found: ignore

type-checking:
name: Type Checking (basedpyright)
Expand Down Expand Up @@ -360,8 +382,17 @@ jobs:
- name: Run type checking
run: |
echo "πŸ” Running basedpyright type checking..."
# Fail on type errors (severity 8) to enforce type safety in CI/CD
hatch run type-check
mkdir -p logs/type-check
TYPE_CHECK_LOG="logs/type-check/type-check_$(date -u +%Y%m%d_%H%M%S).log"
hatch run type-check 2>&1 | tee "$TYPE_CHECK_LOG"
exit "${PIPESTATUS[0]:-$?}"
- name: Upload type-check logs
if: always()
uses: actions/upload-artifact@v4
with:
name: type-check-logs
path: logs/type-check/
if-no-files-found: ignore

linting:
name: Linting (ruff, pylint)
Expand Down Expand Up @@ -401,7 +432,16 @@ jobs:
- name: Run linting
run: |
echo "πŸ” Running linting checks..."
hatch run lint || echo "⚠️ Linting incomplete"
mkdir -p logs/lint
LINT_LOG="logs/lint/lint_$(date -u +%Y%m%d_%H%M%S).log"
hatch run lint 2>&1 | tee "$LINT_LOG" || echo "⚠️ Linting incomplete"
- name: Upload lint logs
if: always()
uses: actions/upload-artifact@v4
with:
name: lint-logs
path: logs/lint/
if-no-files-found: ignore

package-validation:
name: Package Validation (uvx/pip)
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ All notable changes to this project will be documented in this file.

---

## [0.31.1] - 2026-02-16

### Added

- CI log artifacts: PR Orchestrator workflow now uploads test logs (`test-logs`) from `hatch run smart-test-full` and repro logs/reports (`repro-logs`, `repro-reports`) from the contract-first-ci job so failed runs can be debugged by downloading full logs from the Actions Artifacts section without re-running locally.
- Documentation: "CI and GitHub Actions" section in [Troubleshooting](docs/guides/troubleshooting.md) describing artifact names and how to download and use them.

### Changed

- Tests job in `.github/workflows/pr-orchestrator.yml` now runs `hatch run smart-test-full` (single full-suite step with log output to `logs/tests/`) and uploads `logs/tests/` as the `test-logs` artifact.
- Contract-first-ci job captures `specfact repro` stdout/stderr to `logs/repro/` and uploads `repro-logs` and `repro-reports` (`.specfact/reports/enforcement/`) as artifacts on every run.

---

## [0.31.0] - 2026-02-13

### Added
Expand Down
32 changes: 32 additions & 0 deletions docs/guides/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,38 @@ The command automatically uses tokens in this order:

---

## CI and GitHub Actions

### Downloading test and repro logs from CI

When a PR or push runs the **PR Orchestrator** workflow, test and repro output are uploaded as workflow artifacts so you can debug failures without re-running the full suite locally.

1. **Where to find artifacts**

- Open the workflow run: **Actions** β†’ select the **PR Orchestrator** run (e.g. from your PR or branch).
- Scroll to the **Artifacts** section at the bottom of the run.

2. **Artifact names and contents**

| Artifact | Job | Contents |
|----------------------|------------------|---------------------------------------------------------------------------|
| `test-logs` | Tests (Python 3.12) | Full test run and coverage logs from `hatch run smart-test-full` (`logs/tests/`). |
| `coverage-reports` | Tests (Python 3.12) | Coverage XML for quality gates (when unit tests ran). |
| `compat-py311-logs` | Compatibility (Python 3.11) | Pytest and coverage XML output from the compat job. |
| `type-check-logs` | Type Checking (basedpyright) | Full basedpyright type-check output. |
| `lint-logs` | Linting (ruff, pylint) | Full lint run output. |
| `quality-gates-logs`| Quality Gates (Advisory) | Coverage percentage and advisory message. |
| `repro-logs` | Contract-First CI | Full stdout/stderr of `specfact repro` (`logs/repro/`). |
| `repro-reports` | Contract-First CI | Repro report YAMLs from `.specfact/reports/enforcement/`. |

3. **How to use them**

- Download the artifact (e.g. `test-logs` or `repro-logs`) from the run page.
- Unzip and open the log or report files to see the full output that led to the failure.
- Use this instead of copying snippets from the step log, so you get complete error context before fixing and pushing again.

---

## Getting Help

If you're still experiencing issues:
Expand Down
44 changes: 44 additions & 0 deletions docs/plans/ci-pr-orchestrator-log-artifacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Plan: PR Orchestrator β€” Attach Test and Repro Logs to CI Runs

**Repository**: `nold-ai/specfact-cli` (public)

## Purpose

Improve the GitHub Action runner (`.github/workflows/pr-orchestrator.yml`) so that:

1. **Full test output is available on failure** β€” Today we often copy-paste from the UI, error details are truncated or only snippets, and we must re-run locally to find all issues. The goal is to run `smart-test-full` (or equivalent) so that log files are generated and attached to each CI run so we can download them when a run fails.

2. **Repro test logs are captured and attached** β€” For the `specfact repro` step (contract-first-ci job), collect logs and the repro report directory (e.g. `.specfact/reports/enforcement`) and upload them as artifacts so we can download on error.

3. **Shift full execution validation to CI** β€” By having full logs and repro artifacts attached, we can rely on CI for full validation before merging to `dev` and avoid redundant local full runs.

## Current Shortcomings

- **pr-orchestrator.yml** runs contract-first test layers (contract-test-contracts, contract-test-exploration, contract-test-scenarios, contract-test-e2e) but does **not** use `smart-test-full`; it does not write test output to log files that are then uploaded.
- Only **coverage.xml** is uploaded (in Tests job); no raw test logs or repro logs.
- The **contract-first-ci** job runs `hatch run specfact repro --verbose --crosshair-required --budget 120` with `|| echo "SpecFact repro found issues"`, so the job does not fail hard and repro stdout/stderr and reports are not uploaded as artifacts.
- Error details in the GitHub Actions UI are limited to step output (snippets); full logs are not downloadable.

## What Changes

- **Tests job**: Switch to (or add) running `hatch run smart-test-full` so that the smart-test script writes logs under `logs/tests/` (e.g. `full_test_run_*.log`, `full_coverage_*.log`). Upload the contents of `logs/tests/` (and existing `logs/tests/coverage/coverage.xml`) as workflow artifacts so they are available for download on every run (or on failure only to save space/retention).
- **Contract-first-ci job (repro)**: After running `specfact repro`, collect (1) repro stdout/stderr (e.g. by redirecting to a file or using a wrapper that writes to `logs/repro/`), and (2) `.specfact/reports/enforcement/` (repro reports). Upload these as artifacts (e.g. `repro-logs` and `repro-reports`), so on failure we can download full repro output and reports.
- **Artifact upload strategy**: Use `actions/upload-artifact@v4` with a consistent naming scheme (e.g. `test-logs-<job>`, `repro-logs`, `repro-reports`). Optionally use `if: failure()` or `if: always()` so artifacts are retained for failed runs or all runs per project policy.
- **Documentation**: Update docs (e.g. contributing, troubleshooting, or reference) to mention that CI produces downloadable test and repro log artifacts and how to use them.

## Files to Create/Modify

- `.github/workflows/pr-orchestrator.yml` β€” Add/change test step to use smart-test-full with log output; add artifact upload steps for test logs and repro logs/reports.
- Optionally: a small script under `.github/workflows/scripts/` to run repro and capture logs to a file (if not done inline).
- `docs/` β€” Add or update a section on CI artifacts (where to find them, what they contain).

## Success Metrics

- Every PR/push run that executes tests produces downloadable artifacts containing full test log files when using smart-test-full.
- Every run that executes `specfact repro` produces downloadable artifacts containing repro stdout/stderr and repro report files.
- Contributors can diagnose failures from downloaded artifacts without needing to re-run the full suite locally.

## Dependencies

- Existing `tools/smart_test_coverage.py` already writes to `logs/tests/` when running full tests; ensure CI has write access and paths are consistent.
- Existing `specfact.yml` already uploads `.specfact/reports/enforcement/*.yaml` and `.specfact/pr-comment.md`; align naming and behavior with pr-orchestrator for repro artifacts.
6 changes: 6 additions & 0 deletions openspec/CHANGE_ORDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ These are derived extensions of the same 2026-02-15 plan and are required to ope
| validation | 01 | validation-01-deep-validation | [#163](https://github.com/nold-ai/specfact-cli/issues/163) | β€” |
| bundle-mapper | 01 | bundle-mapper-01-mapping-strategy | [#121](https://github.com/nold-ai/specfact-cli/issues/121) | β€” |

### CI/CD (workflow and artifacts)

| Module | Order | Change folder | GitHub # | Blocked by |
|--------|-------|----------------|----------|------------|
| ci | 01 | ci-01-pr-orchestrator-log-artifacts | [#260](https://github.com/nold-ai/specfact-cli/issues/260) | β€” |

### backlog-core (required by all backlog-* modules)

| Module | Order | Change folder | GitHub # | Blocked by |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-16
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Change Validation Report: ci-01-pr-orchestrator-log-artifacts

**Validation Date**: 2026-02-16
**Change Proposal**: [proposal.md](./proposal.md)
**Validation Method**: Dry-run simulation; format and OpenSpec strict validation

## Executive Summary

- Breaking Changes: 0 detected
- Dependent Files: 0 (workflow and docs only; no application code interfaces changed)
- Impact Level: Low
- Validation Result: Pass
- User Decision: N/A (no breaking changes)

## Breaking Changes Detected

None. This change only modifies `.github/workflows/pr-orchestrator.yml` (add/change steps for smart-test-full and artifact uploads) and adds or updates documentation. No public API, contract, or application code changes.

## Dependencies Affected

- **Workflow**: `.github/workflows/pr-orchestrator.yml` β€” add steps; existing jobs (tests, contract-first-ci) extended with new steps. No other workflows depend on pr-orchestrator's internal step names.
- **Docs**: One new or updated section (troubleshooting or contributing). No code imports or references to workflow artifact names outside docs.

## Impact Assessment

- **Code Impact**: None (workflow YAML and docs only).
- **Test Impact**: None; optional manual verification that artifacts appear in a CI run.
- **Documentation Impact**: New or updated subsection on CI artifacts (where to find them, what they contain).
- **Release Impact**: Patch (CI improvement, no user-facing API change).

## Format Validation

- **proposal.md Format**: Pass
- Title: `# Change: CI β€” Attach Test and Repro Log Artifacts...` (correct).
- Required sections: Why, What Changes, Capabilities, Impact, Source Tracking present.
- Capabilities section: One capability `ci-log-artifacts` with spec file `specs/ci-log-artifacts/spec.md`.
- **tasks.md Format**: Pass
- TDD/SDD order section at top; hierarchical numbered tasks; branch creation first (## 1), PR creation last (## 9).
- Sub-tasks use `- [ ] N.M.N` format.
- **specs Format**: Pass
- `specs/ci-log-artifacts/spec.md` uses Given/When/Then; ADDED requirements with scenarios.
- **design.md Format**: Pass
- Overview, current/target state, integration points, edge cases; no bridge adapters (N/A).
- **Config.yaml Compliance**: Pass
- Git workflow: branch first, PR last; quality gates; documentation task; version/changelog before PR.
- No GitHub issue creation task in tasks.md; proposal has Source Tracking placeholder for issue (to be created).

## OpenSpec Validation

- **Status**: Pass
- **Validation Command**: `openspec validate ci-01-pr-orchestrator-log-artifacts --strict`
- **Issues Found**: 0
- **Issues Fixed**: 0
- **Re-validated**: N/A

## Next Steps

1. Create GitHub issue in nold-ai/specfact-cli (title: `[Change] Attach test and repro log artifacts to PR orchestrator runs`; labels: enhancement, change-proposal).
2. Update proposal.md Source Tracking with issue number and URL.
3. Proceed with implementation: `/opsx:apply ci-01-pr-orchestrator-log-artifacts` or apply tasks manually.
4. Update CHANGE_ORDER.md when change is created (add row under a CI or cross-cutting section).
Loading
Loading