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
12 changes: 12 additions & 0 deletions .github/instruction-surfaces.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@
"review_owner": "z-shell maintainers",
"canonical_for": []
},
{
"id": "decision-0042",
"path": "decisions/0042-organization-reusable-workflows-at-main.md",
"kind": "decision",
"authority": "advisory",
"consumers": ["agent", "human"],
"tasks": ["architecture-decision"],
"file_patterns": ["**"],
"required": false,
"review_owner": "z-shell maintainers",
"canonical_for": []
},
{
"id": "organization-patterns",
"path": "PATTERNS.md",
Expand Down
23 changes: 11 additions & 12 deletions .github/instructions/ci/workflow-contract.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ permissions:

### Action Pinning

- **Immutable Commit SHAs**: Every remote action and reusable workflow `uses:` reference, including organization references, MUST be pinned to a full 40-character commit SHA.
- **Version Comments**: Append the associated release version after the SHA for auditability (e.g., `# v4.3.1`). For organization reusable workflows from `z-shell/.github/.github/workflows/` without an adopted workflow release, retain the established interim `# main` comment. The comment identifies the source branch; the full SHA selects the code.
- **On the source branch**: Pin an organization reusable workflow only to a commit on the default branch of `z-shell/.github`, never to a pull request's branch commit. A squash merge leaves that commit on no branch, and callers pinned to it end every run in a startup failure with no jobs ([#774](https://github.com/z-shell/.github/issues/774)). Before pinning, `gh api repos/z-shell/.github/compare/main...SHA --jq .status` must print `behind` or `identical`. The daily Community Health Audit reports any other pin (`repo-settings-audit.rb --workflow-pins`).
- **Prohibited**: Never use mutable tags (e.g., `@v4`, `@main`, `@latest`).
- **Organization reusable workflows**: Reference reusable workflows from `z-shell/.github/.github/workflows/` at `@main` ([ADR-0042](../../../decisions/0042-organization-reusable-workflows-at-main.md)). A change merged to the protected `main` of `z-shell/.github` then reaches every caller without a pull request in each repository. This covers only those reusable workflows; organization actions under `z-shell/.github/actions/` are pinned like any other action. The organization Renovate preset (`renovate-config.json`) leaves these `@main` references unpinned.
- **Immutable Commit SHAs**: Every other remote action and reusable workflow `uses:` reference MUST be pinned to a full 40-character commit SHA.
- **Version Comments**: Append the associated release version after the SHA for auditability (e.g., `# v4.3.1`).
- **Existing SHA pins**: A caller that pins an organization reusable workflow to a full SHA with the earlier interim `# main` comment remains valid until it moves to `@main` in its own reviewed change. Such a pin must name a commit on the default branch of `z-shell/.github`, never a pull request's branch commit. A squash merge leaves that commit on no branch, and callers pinned to it end every run in a startup failure with no jobs ([#774](https://github.com/z-shell/.github/issues/774)). Before pinning, `gh api repos/z-shell/.github/compare/main...SHA --jq .status` must print `behind` or `identical`. The daily Community Health Audit reports any other pin (`repo-settings-audit.rb --workflow-pins`).
- **Prohibited**: Never use mutable tags or branches (e.g., `@v4`, `@main`, `@latest`) for anything other than organization reusable workflows.

```yaml
# Correct
Expand All @@ -55,13 +56,11 @@ permissions:
- uses: actions/checkout@v4
```

### Interim pinact exception for organization reusable workflows
### pinact exception for organization reusable workflows

pinact 4.1.1 rejects the interim `# main` comment on a SHA pin because it requires a version comment. Reuse [the shared pinact template](../../../templates/pinact/.pinact.yaml), observed in [zunit#27](https://github.com/z-shell/zunit/pull/27) and [zpmod#114](https://github.com/z-shell/zpmod/pull/114). Copy it to the caller repository's `.pinact.yaml` only when no pinact configuration exists. Otherwise merge the `ignore_actions` entry into the existing configuration, preserving its settings and avoiding a second competing config file. The template retains configuration version 3; it does not install or enable pinact.
pinact 4.1.1 would pin an `@main` reference, and it rejects the `# main` comment on an existing SHA pin because it requires a version comment. Reuse [the shared pinact template](../../../templates/pinact/.pinact.yaml), observed in [zunit#27](https://github.com/z-shell/zunit/pull/27) and [zpmod#114](https://github.com/z-shell/zpmod/pull/114). Copy it to the caller repository's `.pinact.yaml` only when no pinact configuration exists. Otherwise merge its `ignore_actions` entries into the existing configuration, preserving its settings and avoiding a second competing config file. The template retains configuration version 3; it does not install or enable pinact.

The exception matches only `z-shell/.github/.github/workflows/` references with a full lowercase 40-hex SHA. Mutable refs, other organization actions and third-party actions remain subject to pinact's checks. pinact skips all validation and updates for matching entries, not just the comment check: the regex restricts the ref's shape but does not verify the commit exists, belongs to the repository, or has been reviewed. Continue to verify the selected workflow and commit through the repository's checks and PR review. Do not broaden the exception or use a branch or tag as the actual ref.

This is an interim compatibility measure for [#663](https://github.com/z-shell/.github/issues/663). Once [#543](https://github.com/z-shell/.github/issues/543) establishes the applicable immutable workflow releases and a caller adopts them, use the associated workflow release comment and remove its unused exception. Downstream adoption and release changes require their own reviewed rollout. See [pinact 4.1.1 configuration](https://github.com/suzuki-shunsuke/pinact/blob/v4.1.1/docs/config.md#ignore_actions) for exception semantics.
The exception matches only `z-shell/.github/.github/workflows/` references whose ref is exactly `main` or a full lowercase 40-hex SHA; pinact matches both patterns against the whole name and ref. Other organization actions and third-party actions remain subject to pinact's checks. pinact skips all validation and updates for matching entries: for a SHA pin the regex restricts the ref's shape but does not verify the commit exists, belongs to the repository, or has been reviewed. Do not broaden the exception to other repositories, actions or refs. See [pinact 4.1.1 configuration](https://github.com/suzuki-shunsuke/pinact/blob/v4.1.1/docs/config.md#ignore_actions) for exception semantics.

### Permissions (Least Privilege)

Expand All @@ -88,7 +87,7 @@ This is an interim compatibility measure for [#663](https://github.com/z-shell/.
- Explicitly declare `type` and `required` for every input in `workflow_call`.
- Declare `default` only for optional inputs. A `required: true` input must not carry one, because the caller always supplies the value and the default is unreachable.
- When a workflow is **also** triggered directly (`push`, `pull_request`, `schedule`), put the operative fallback in the job step, for example `: "${VAR:=...}"`. The `inputs` context holds "the inputs of a reusable or manually triggered workflow", so on a direct trigger it is empty and `workflow_call` defaults are never applied. A default declared on the input is then dead text on the path the workflow actually takes, and an empty pattern reaching `grep -E` matches every line.
- Reference called workflows using pinned immutable refs.
- Reference organization reusable workflows at `@main` and any other called workflow at a pinned immutable ref.
- Expose job `outputs` cleanly for downstream dependent jobs (`needs:`).

The shared `zsh-ci.yml` owns native syntax and optional compilation. Callers
Expand Down Expand Up @@ -126,6 +125,6 @@ new uses; migrate existing uses through their owning rollout and runbook.
- [ ] Workflow `name:` and Job `name:` contain NO emojis.
- [ ] Top-level `permissions:` is declared with minimum necessary scope.
- [ ] Concurrency follows the execution-control rules above, including documented required-check exceptions and distinct caller/callee groups.
- [ ] All remote actions and reusable workflows are pinned to 40-character commit SHAs with associated release comments, or the documented interim `# main` comment for organization reusable workflows.
- [ ] Any interim pinact exception uses the shared template's exact repository/workflow and full-SHA match; the selected workflow and commit are verified separately.
- [ ] Organization reusable workflows from `z-shell/.github/.github/workflows/` are referenced at `@main` (or keep an existing full-SHA pin on the default branch); every other remote action and reusable workflow is pinned to a 40-character commit SHA with its release comment.
- [ ] Any pinact exception uses the shared template's exact repository/workflow and its `main` or full-SHA ref match.
- [ ] Actionlint and YAML syntax checks pass.
6 changes: 3 additions & 3 deletions PATTERNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ Observed in:

Pattern:

- Pin all remote GitHub Action and reusable workflow references, external and organization-owned, to a full 40-character commit SHA.
- Append the associated release version comment. Organization reusable workflows from `z-shell/.github/.github/workflows/` without an adopted workflow release retain the interim `# main` comment; it does not change the immutable ref.
- Pin all remote GitHub Action and reusable workflow references to a full 40-character commit SHA, except organization reusable workflows from `z-shell/.github/.github/workflows/`, which are referenced at `@main` (ADR-0042). Organization actions under `z-shell/.github/actions/` are pinned.
- Append the associated release version comment to each SHA pin. An existing organization reusable workflow SHA pin keeps its interim `# main` comment until it moves to `@main`.

The pinact exception for that interim workflow comment is observed in `z-shell/zunit:.pinact.yaml` and `z-shell/zpmod:.pinact.yaml`. Follow the [canonical CI pinning guidance](.github/instructions/ci/workflow-contract.instructions.md#interim-pinact-exception-for-organization-reusable-workflows) and reuse its linked template rather than creating another exception shape.
The pinact exception for organization reusable workflows is observed in `z-shell/zunit:.pinact.yaml` and `z-shell/zpmod:.pinact.yaml`. Follow the [canonical CI pinning guidance](.github/instructions/ci/workflow-contract.instructions.md#pinact-exception-for-organization-reusable-workflows) and reuse its linked template rather than creating another exception shape.

```yaml
# Good: pinned to SHA with version comment
Expand Down
2 changes: 1 addition & 1 deletion automation/ci/test_validate_zsh_standard_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1992,7 +1992,7 @@ def test_repair_2_consumer_parser_outputs_match_frozen_golden(self) -> None:

self.assertEqual(
digest,
"2f731c3c041633aa43a9cdfbd5b1d024bdc9762ab3ba48b70d865b8a3f3984ae",
"7e904244848331b7e2471354db7ccb4c8c0ab317c9a071d2ca0d05a0ed40edfb",
msg=(
"The frozen golden covers the parsed output of every path in "
f"{paths}. Editing any of them changes this digest, which is "
Expand Down
43 changes: 43 additions & 0 deletions decisions/0042-organization-reusable-workflows-at-main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 42. Reference Organization Reusable Workflows at main

- **Status:** ACCEPTED
- **Date:** 2026-10-16
- **Deciders:** ss-o
- **Supersedes:** None
- **Superseded by:** None

## Context

The workflow contract required every `uses:` reference, including the organization's own reusable workflows in `z-shell/.github/.github/workflows/`, to be pinned to a full commit SHA, with an interim `# main` comment until those workflows had releases. Callers therefore had to change their pin to receive any change to a shared workflow. With shared workflows used across the organization's repositories, every change to one meant a pull request in each calling repository, and pins drifted apart in the meantime: callers of the same workflow ran different revisions of it.

The starter templates in `workflow-templates/` already reference the shared workflows at `@main`, so new repositories started outside the written rule.

The shared workflows live in `z-shell/.github`, whose `main` is protected and changes only through reviewed pull requests. Third-party actions are outside that review.

## Decision

1. Callers reference reusable workflows from `z-shell/.github/.github/workflows/` at `@main`. A change merged to `z-shell/.github` `main` reaches every caller on its next run.
2. Every other remote reference stays pinned to a full 40-character commit SHA with its release comment: third-party actions, third-party reusable workflows, and organization actions under `z-shell/.github/actions/`.
3. Existing full-SHA pins of organization reusable workflows remain valid until each caller moves to `@main` in its own reviewed change. Such a pin must still name a commit on the default branch ([#774](https://github.com/z-shell/.github/issues/774)).
4. The shared pinact template accepts both an exact `main` ref and a full SHA for organization reusable workflows only, and the organization Renovate preset does not pin `z-shell/.github` references whose ref is `main`.

## Consequences

- A shared workflow change needs one reviewed pull request in `z-shell/.github` instead of one in each calling repository, and all callers run the same revision.
- A defect merged to a shared workflow reaches every caller at once. Review on `z-shell/.github` is the control for that risk; a caller that must freeze a revision can still pin a default-branch SHA under point 3.
- Changing a shared workflow's inputs, outputs or required permissions affects every caller on its next run, so such changes need the same care as an interface change: keep them backward compatible or coordinate the callers first.
- Moving existing callers from SHA pins to `@main` is separate, per-repository work.

## Acceptance

Accepted by ss-o on 2026-10-16.

## Alternatives considered

1. Keep SHA pins and let Renovate propose digest updates: keeps each caller on reviewed code, but still produces a pull request per repository per shared change, and callers diverge until each one merges.
2. Versioned workflow releases (tags) referenced by SHA with a release comment: makes revisions explicit, but adds a release step to every shared change and still needs a pin update in each caller.

## References

- [Workflow contract: Action Pinning](../knowledge/domains/ci/workflow-contract.md).
- [ADR-0009](0009-testing-ci-strategy.md), which defers workflow conventions to the contract.
1 change: 1 addition & 0 deletions decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ a maintainer moves a record from `PROPOSED` to `ACCEPTED`.
| [0039](0039-zi-main-integration-and-signed-milestones.md) | Zi Main Integration and Signed Milestones | ACCEPTED | 2026-10-08 | ss-o |
| [0040](0040-central-project-profiles-for-issue-intake.md) | Central Project Profiles for Issue Intake | ACCEPTED | 2026-10-09 | ss-o |
| [0041](0041-zsh-lint-parser-root-module-packaging.md) | Package the zsh-lint parser fork inside the root Go module | ACCEPTED | 2026-10-15 | ss-o |
| [0042](0042-organization-reusable-workflows-at-main.md) | Reference Organization Reusable Workflows at main | ACCEPTED | 2026-10-16 | ss-o |
6 changes: 3 additions & 3 deletions knowledge/domains/agents/implementation-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ Observed in:

Pattern:

- Pin all remote GitHub Action and reusable workflow references, external and organization-owned, to a full 40-character commit SHA.
- Append the associated release version comment. Organization reusable workflows from `z-shell/.github/.github/workflows/` without an adopted workflow release retain the interim `# main` comment; it does not change the immutable ref.
- Pin all remote GitHub Action and reusable workflow references to a full 40-character commit SHA, except organization reusable workflows from `z-shell/.github/.github/workflows/`, which are referenced at `@main` (ADR-0042). Organization actions under `z-shell/.github/actions/` are pinned.
- Append the associated release version comment to each SHA pin. An existing organization reusable workflow SHA pin keeps its interim `# main` comment until it moves to `@main`.

The pinact exception for that interim workflow comment is observed in `z-shell/zunit:.pinact.yaml` and `z-shell/zpmod:.pinact.yaml`. Follow the [canonical CI pinning guidance](../../../.github/instructions/ci/workflow-contract.instructions.md#interim-pinact-exception-for-organization-reusable-workflows) and reuse its linked template rather than creating another exception shape.
The pinact exception for organization reusable workflows is observed in `z-shell/zunit:.pinact.yaml` and `z-shell/zpmod:.pinact.yaml`. Follow the [canonical CI pinning guidance](../../../.github/instructions/ci/workflow-contract.instructions.md#pinact-exception-for-organization-reusable-workflows) and reuse its linked template rather than creating another exception shape.

```yaml
# Good: pinned to SHA with version comment
Expand Down
1 change: 1 addition & 0 deletions knowledge/domains/ci/repository-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Select a file for the task described below. Imported files have an editable know
| [decisions/0004-dependabot-unification.md](../../../decisions/0004-dependabot-unification.md) | referenced | Historical decision record or generated decision index; inspect recorded status and supersession before applying it. Preserve numbering and accepted history. |
| [decisions/0005-workflow-naming-conventions.md](../../../decisions/0005-workflow-naming-conventions.md) | referenced | Historical decision record or generated decision index; inspect recorded status and supersession before applying it. Preserve numbering and accepted history. |
| [decisions/0012-hybrid-dependency-management.md](../../../decisions/0012-hybrid-dependency-management.md) | referenced | Historical decision record or generated decision index; inspect recorded status and supersession before applying it. Preserve numbering and accepted history. |
| [decisions/0042-organization-reusable-workflows-at-main.md](../../../decisions/0042-organization-reusable-workflows-at-main.md) | referenced | Accepted decision to reference organization reusable workflows at main while every other remote reference stays SHA-pinned. |
| [renovate-config.json](../../../renovate-config.json) | referenced | Executable Renovate configuration; inspect with dependency-management guidance. This inventory does not change bot ownership or update policy. |
| [renovate-zsh-lint.json](../../../renovate-zsh-lint.json) | referenced | Opt-in Renovate preset for pinned zsh-lint releases; inspect with dependency-management guidance. This inventory does not change bot ownership or update policy. |
| [runbooks/dependency-management.md](../../../runbooks/dependency-management.md) | [Imported source](dependency-management.md) | Edit the declared knowledge source; preserve the complete native consumer through checked generation. |
Expand Down
Loading
Loading