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
136 changes: 136 additions & 0 deletions .github/workflows/refresh-decision-prepare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
name: Refresh Decision Prepare

"on":
workflow_dispatch:
inputs:
package_ids:
description: "Comma-separated package IDs to compare."
required: true
default: "xyflow.workspace,xyflow.react,xyflow.svelte,xyflow.system"
package_id:
description: "Decision subject package ID."
required: true
default: "xyflow.workspace"
version:
description: "Package version to compare."
required: true
default: "0.1.0"
source_repository:
description: "Fresh upstream source repository URL."
required: true
default: "https://github.com/xyflow/xyflow"
source_revision:
description: "Fresh upstream source revision as a 40-character commit SHA."
required: true
default: "a58568f11bc0e1a1bdca1b3549e959e2e1ca0cdd"
fresh_generated_root:
description: "Fresh generated artifact root containing <package_id>/<version>."
required: true
default: "public-index/generated"
run_label:
description: "Human-readable label for the refresh evaluation."
required: true
default: "manual-refresh-decision-prepare"

permissions:
contents: read

jobs:
prepare-refresh-decision:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install SpecPM
run: python -m pip install -e ".[dev]"

- name: Prepare refresh decision artifact
env:
PACKAGE_IDS: ${{ inputs.package_ids }}
PACKAGE_ID: ${{ inputs.package_id }}
VERSION: ${{ inputs.version }}
SOURCE_REPOSITORY: ${{ inputs.source_repository }}
SOURCE_REVISION: ${{ inputs.source_revision }}
FRESH_GENERATED_ROOT: ${{ inputs.fresh_generated_root }}
RUN_LABEL: ${{ inputs.run_label }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/refresh-decision"
python - <<'PY'
import os
import subprocess
import sys
from pathlib import Path

output_root = Path(os.environ["RUNNER_TEMP"]) / "refresh-decision"
decision_path = output_root / "refresh-decision.json"
report_path = output_root / "prepare-report.json"
package_ids = [
value.strip()
for value in os.environ["PACKAGE_IDS"].replace("\n", ",").split(",")
if value.strip()
]
if not package_ids:
raise SystemExit("package_ids input must contain at least one package ID")

command = [
sys.executable,
"-m",
"specpm.cli",
"producer-bundle",
"prepare-refresh-decision",
"--root",
os.environ["GITHUB_WORKSPACE"],
"--fresh-generated-root",
os.environ["FRESH_GENERATED_ROOT"],
"--package-id",
os.environ["PACKAGE_ID"],
"--version",
os.environ["VERSION"],
"--source-repository",
os.environ["SOURCE_REPOSITORY"],
"--source-revision",
os.environ["SOURCE_REVISION"],
"--run-label",
os.environ["RUN_LABEL"],
"--review-location",
f"{os.environ['GITHUB_SERVER_URL']}/{os.environ['GITHUB_REPOSITORY']}/actions/runs/{os.environ['GITHUB_RUN_ID']}",
"--output",
str(decision_path),
"--json",
]
for package_id in package_ids:
command.extend(["--package", package_id])

completed = subprocess.run(command, capture_output=True, text=True, check=False)
report_path.write_text(completed.stdout, encoding="utf-8")
if completed.stdout:
print(completed.stdout)
if completed.stderr:
print(completed.stderr, file=sys.stderr)
raise SystemExit(completed.returncode)
PY

- name: Preflight prepared refresh decision
run: |
python -m specpm.cli producer-bundle preflight-refresh-decision \
--body "$RUNNER_TEMP/refresh-decision/refresh-decision.json" \
--root "$GITHUB_WORKSPACE" \
--json \
> "$RUNNER_TEMP/refresh-decision/preflight-report.json"
cat "$RUNNER_TEMP/refresh-decision/preflight-report.json"

- name: Upload refresh decision artifact
uses: actions/upload-artifact@v7
with:
name: generated-candidate-refresh-decision
path: |
${{ runner.temp }}/refresh-decision/refresh-decision.json
${{ runner.temp }}/refresh-decision/prepare-report.json
${{ runner.temp }}/refresh-decision/preflight-report.json
3 changes: 3 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ tree with `specpm producer-bundle prepare-refresh-decision`, compare
contract-bearing generated files, and emit read-only
`SpecPMGeneratedCandidateRefreshDecisionPrepareReport` evidence before any
maintainer registry decision.
The manual `Refresh Decision Prepare` GitHub Actions workflow now packages that
same refresh decision evidence as CI artifacts without write credentials or
registry mutation.
SpecPM now has a consumer-side `preflight-ai-draft` gate for
`SpecHarvesterPackageSetAIDraftProposal`. It verifies AI-proposed member
selection, exclusions, and `contains` relations against deterministic workspace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ Matching contract files produce `status: no_update_required`,
revision, or accepted-artifact drift produces `manual_review_required` with
`updateNeeded: true`. Both outcomes remain review evidence only.

## CI Artifact

The manual `.github/workflows/refresh-decision-prepare.yml` workflow runs the
same prepare helper through `workflow_dispatch` and uploads:

- `refresh-decision.json`
- `prepare-report.json`
- `preflight-report.json`

It defaults to the current `xyflow` package-set no-op comparison and exposes
inputs for package IDs, version, source repository, source revision, fresh
generated root, and run label. The workflow has `contents: read` permissions,
does not require write credentials, and does not mutate registry state.

## Consumer-Side Preflight

SpecPM can verify refresh decision records with:
Expand Down
3 changes: 3 additions & 0 deletions Sources/SpecPM/Documentation.docc/GitHubActionsPermissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ separate GitHub Pages deploy job with `pages: write` and `id-token: write`.
The producer bundle preflight workflow is a read-only pull request check: it
only runs consumer-side evidence validation when a pull request body includes
producer bundle evidence blocks.
The refresh decision prepare workflow is also read-only: it runs only on manual
dispatch, prepares/preflights generated candidate refresh decision artifacts,
and uploads those artifacts for maintainer review without registry mutation.

The only deployment secrets currently allowed are `FTP_HOST`, `FTP_PORT`,
`FTP_USER`, `FTP_PASS`, and `FTP_REMOTE_ROOT`. They are scoped to the `FTP`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ consumer-side preflight checks. It is read-only review evidence and does not
write accepted packages, generated candidates, relations, or public registry
metadata.

The manual `.github/workflows/refresh-decision-prepare.yml` workflow wraps this
helper as a dry-run artifact producer. It runs with `contents: read`, uploads
the prepared decision plus prepare/preflight reports, and does not require
registry write credentials.

## Fixture Alignment

Cross-repository fixture ownership and drift handling are defined in
Expand Down
3 changes: 3 additions & 0 deletions Sources/SpecPM/Documentation.docc/Roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ tree with `specpm producer-bundle prepare-refresh-decision`, compare
contract-bearing generated files, and emit read-only
`SpecPMGeneratedCandidateRefreshDecisionPrepareReport` evidence before any
maintainer registry decision.
The manual `Refresh Decision Prepare` GitHub Actions workflow now packages that
same refresh decision evidence as CI artifacts without write credentials or
registry mutation.
SpecPM now has a consumer-side `preflight-ai-draft` gate for
`SpecHarvesterPackageSetAIDraftProposal`. It verifies AI-proposed member
selection, exclusions, and `contains` relations against deterministic workspace
Expand Down
1 change: 1 addition & 0 deletions specpm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ index:
- specpm.registry.generated_candidate_refresh_decision_policy
- specpm.registry.generated_candidate_refresh_decision_preflight
- specpm.registry.generated_candidate_refresh_decision_prepare
- specpm.registry.generated_candidate_refresh_decision_ci_artifact
- specpm.registry.index_removal_request_flow
- specpm.registry.namespace_claim_request_flow
- specpm.registry.namespace_claim_policy
Expand Down
16 changes: 16 additions & 0 deletions specs/GENERATED_CANDIDATE_REFRESH_DECISION_POLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ artifact assumptions differ, the draft uses `manual_review_required` and
`updateNeeded: true`; it still does not mutate curated artifacts, generated
artifacts, relations, or registry metadata.

## CI Artifact

The manual `.github/workflows/refresh-decision-prepare.yml` workflow runs the
same prepare helper through `workflow_dispatch` and uploads three review
artifacts:

- `refresh-decision.json`
- `prepare-report.json`
- `preflight-report.json`

The workflow defaults to the current `xyflow` package-set no-op comparison, but
maintainers can override package IDs, version, source repository, source
revision, fresh generated root, and run label. It has `contents: read`
permissions, does not require write credentials, and does not mutate accepted
packages, generated candidates, accepted relations, or registry metadata.

## Consumer-Side Preflight

SpecPM can verify a generated candidate refresh decision before maintainers use
Expand Down
11 changes: 7 additions & 4 deletions specs/GITHUB_ACTIONS_PERMISSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ all secrets as privileged and keeps explicit workflow boundaries around them.
| `.github/workflows/package-submission-check.yml` | `issues` labeled `package-submission` | `contents: read`, `issues: write`. | No repository or environment secrets. | Validate public package submission text and post/update one validation comment. |
| `.github/workflows/package-submission-triage.yml` | `issues` labeled `package-submission` | `contents: read`, `issues: write`. | No repository or environment secrets. | Prepare review labels and an idempotent maintainer guidance comment. |
| `.github/workflows/producer-bundle-preflight.yml` | `pull_request` | `contents: read`. | No repository or environment secrets. | If the pull request body includes producer bundle evidence blocks, run consumer-side `specpm producer-bundle preflight`; otherwise report a skip. |
| `.github/workflows/refresh-decision-prepare.yml` | `workflow_dispatch` | `contents: read`. | No repository or environment secrets. | Prepare and preflight generated candidate refresh decision artifacts for maintainer review. |
| `.github/workflows/namespace-claim-triage.yml` | `issues` labeled `namespace-claim` | `contents: read`, `issues: write`. | No repository or environment secrets. | Prepare namespace review labels and an idempotent policy comment. |
| `.github/workflows/namespace-claim-decision-report.yml` | `issues` labeled/unlabeled `namespace-claim` | `contents: read`, `issues: write`. | No repository or environment secrets. | Report maintainer-applied namespace decision labels. |
| `.github/workflows/namespace-claim-decision-summary.yml` | `workflow_dispatch`, schedule | `contents: read`, `issues: read`. | No repository or environment secrets. | Write read-only namespace decision summaries and upload workflow artifacts. |
Expand All @@ -68,10 +69,12 @@ by:
- `.github/workflows/deploy-connection-check.yml`, job
`deploy-connection-check`.

Package submission, producer bundle preflight, and namespace claim workflows
must not read repository or environment secrets. They process public GitHub
Issue or pull request content and may use `GITHUB_TOKEN` only for the documented
comment, label, search, artifact, and read-only checkout operations.
Package submission, producer bundle preflight, refresh decision prepare, and
namespace claim workflows must not read repository or environment secrets. They
process public GitHub Issue or pull request content, checked-in generated
artifacts, or maintainer-provided workflow inputs, and may use `GITHUB_TOKEN`
only for the documented comment, label, search, artifact, and read-only checkout
operations.

## `pull_request_target` Review Rules

Expand Down
2 changes: 2 additions & 0 deletions specs/MULTI_PACKAGE_PRODUCER_INTAKE.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ When a fresh package-set run is available, maintainers can use
from generated contract-file comparisons before running
`preflight-refresh-decision`. This remains evidence preparation, not package or
relation acceptance.
For a GitHub Actions artifact trail, the manual `Refresh Decision Prepare`
workflow can upload the prepared decision and prepare/preflight reports.

## Bundle-Set Checklist

Expand Down
5 changes: 5 additions & 0 deletions specs/PRODUCER_BUNDLE_PROPOSAL_POLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ consumer-side preflight checks. It is read-only review evidence and does not
write accepted packages, generated candidates, relations, or public registry
metadata.

The manual `.github/workflows/refresh-decision-prepare.yml` workflow wraps this
helper as a dry-run artifact producer. It runs with `contents: read`, uploads
the prepared decision plus prepare/preflight reports, and does not require
registry write credentials.

## Fixture Alignment

Cross-repository fixture ownership and drift handling are defined in
Expand Down
5 changes: 5 additions & 0 deletions specs/PUBLIC_INDEX_OPERATOR_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ files, emits `SpecPMGeneratedCandidateRefreshDecisionPrepareReport`, writes the
draft decision with `--output`, and performs the same preflight checks. It is
read-only review evidence.

For an auditable GitHub Actions run, use the manual
`Refresh Decision Prepare` workflow. It uploads `refresh-decision.json`,
`prepare-report.json`, and `preflight-report.json` as review artifacts with
read-only repository permissions.

When a `SpecPMGeneratedCandidateRefreshDecision` record is available,
maintainers should run:

Expand Down
39 changes: 39 additions & 0 deletions specs/WORKPLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,45 @@ Result:
CLI reference, DocC, roadmap, and self-spec document the helper and
non-authority boundary.

### P66-T21. Generated Candidate Refresh Decision CI Artifact

Status: Completed.

Motivation:

- P66-T20 gives maintainers a local prepare helper, but review should also have
an auditable GitHub Actions path that produces the same machine-readable
evidence.
- CI artifact generation should not require registry write credentials or look
like package acceptance.

Goal:

- Add a manual `Refresh Decision Prepare` workflow that runs
`specpm producer-bundle prepare-refresh-decision`, then runs
`preflight-refresh-decision` against the prepared output.
- Upload `refresh-decision.json`, `prepare-report.json`, and
`preflight-report.json` as workflow artifacts.
- Keep workflow permissions read-only and default inputs aligned with the real
`xyflow` package-set no-op comparison.

Expected result:

- Maintainers can run a dry-run refresh evaluation from GitHub Actions and
attach the resulting artifact bundle to review.
- The workflow is explicit `workflow_dispatch`, has `contents: read`, and does
not mutate accepted packages, generated candidates, accepted relations, or
public registry metadata.

Result:

- `.github/workflows/refresh-decision-prepare.yml` prepares and preflights the
refresh decision artifact bundle.
- Policy docs, operator guide, DocC, roadmap, and self-spec document that the
workflow is evidence-only and credential-free.
- Regression coverage parses the workflow and checks manual dispatch inputs,
read-only permissions, prepare/preflight commands, and artifact upload paths.

## Post-MVP Tracks

- Remote registry service implementation.
Expand Down
Loading