docs(integration): quick-start must show secrets: inherit for ALL consumers (γ path under-specified — Surveyor dec5) #135

Closed
opened 2026-06-26 22:31:03 +02:00 by quartermaster · 2 comments

The gap

Per Surveyor dec5 review on the first external consumer adoption (frankenbit/cellblock#168 + 10287cf), docs/integration.md's quick-start under-specifies the workflow_call secrets contract for path-γ (manifest-via-PR, no RELEASE_TOOLKIT_TOKEN) consumers.

The substrate fact

.forgejo/workflows/_release.yml references secrets.RELEASE_TOOLKIT_TOKEN unconditionally (lines 189 + 228 — both decide + act steps' env blocks). The bash layer falls back to GITHUB_TOKEN via :- when the secret resolves empty, BUT:

  • Forgejo doesn't auto-pass custom secrets to reusable workflows (only GITHUB_TOKEN is auto-available)
  • For the reference to resolve cleanly (even to empty), the consumer must declare secrets: inherit on the workflow_call
  • Without secrets: inherit, Forgejo's expression-engine behavior on the missing secret reference is UNTESTED — per release-toolkit#89, Forgejo's workflow_call secrets handling is finicky

How the gap surfaced

Toolkit-self always runs with secrets: inherit because it dogfoods path-α with RELEASE_TOOLKIT_TOKEN provisioned. So the toolkit has only ever exercised the with-inherit shape. The integration.md quick-start (lines 211-219 in docs/integration.md) shows the consumer wiring WITHOUT secrets: inherit, mentions it only in the path-α section (where it's natural because the consumer is provisioning a token).

A path-γ consumer following the quick-start lands on a shape the toolkit has never run.

Proposed fix

Docs (this tracker's scope)

Update docs/integration.md quick-start to show secrets: inherit on the workflow_call for ALL consumers, with a short comment noting why:

jobs:
  release:
    uses: frankenbit/release-toolkit/.forgejo/workflows/_release.yml@vX.Y.Z
    secrets: inherit  # toolkit references secrets.RELEASE_TOOLKIT_TOKEN
                      # unconditionally; path-γ consumers still need the
                      # reference to resolve — bash :- falls back to
                      # GITHUB_TOKEN (ADR-0007 graceful-degradation)
    with:
      toolkit_ref:   vX.Y.Z
      ...

Same edit applies to the consumer template at .forgejo/workflows/release.yml (toolkit-self's own consumer wrapper) — actually, toolkit-self's own wrapper ALREADY has secrets: inherit; the integration.md template is the under-specified one.

Optional substrate hardening

The unconditional secrets.RELEASE_TOOLKIT_TOKEN reference could be made conditional on the env-block level (only declare the env var when path-α conditions hold), eliminating the inherit requirement for path-γ consumers. This is a substrate change with its own design space; not blocking the docs fix.

Test seam consideration

A bats-level test for "consumer template includes secrets: inherit" could be added as a regression guard (similar to the workflows.bats schema checks).

Refs

  • Surfacing: Surveyor dec5 review on cellblock#168 (10287cf) — first external consumer adoption surfaced this exact "docs-more-specific-to-toolkit-self" class catch
  • The hot fix on cellblock: cellblock#168 follow-up commit adds secrets: inherit to cellblock's release.yml inline
  • Related substrate: ADR-0007 (path-α token override + bash-level GITHUB_TOKEN fallback)
  • Related substrate-state-care: release-toolkit#89 (Forgejo's workflow_call secrets handling finickiness)
  • Cross-repo coupling discipline: this is the abstraction-boundary catch from the first external consumer — exactly the test value c28c framing predicted
## The gap Per Surveyor dec5 review on the first external consumer adoption (frankenbit/cellblock#168 + 10287cf), `docs/integration.md`'s quick-start under-specifies the workflow_call secrets contract for path-γ (manifest-via-PR, no `RELEASE_TOOLKIT_TOKEN`) consumers. ### The substrate fact `.forgejo/workflows/_release.yml` references `secrets.RELEASE_TOOLKIT_TOKEN` **unconditionally** (lines 189 + 228 — both decide + act steps' env blocks). The bash layer falls back to GITHUB_TOKEN via `:-` when the secret resolves empty, BUT: - Forgejo doesn't auto-pass custom secrets to reusable workflows (only `GITHUB_TOKEN` is auto-available) - For the reference to **resolve cleanly** (even to empty), the consumer must declare `secrets: inherit` on the workflow_call - Without `secrets: inherit`, Forgejo's expression-engine behavior on the missing secret reference is UNTESTED — per release-toolkit#89, Forgejo's workflow_call secrets handling is finicky ### How the gap surfaced Toolkit-self **always** runs with `secrets: inherit` because it dogfoods path-α with `RELEASE_TOOLKIT_TOKEN` provisioned. So the toolkit has only ever exercised the with-inherit shape. The integration.md quick-start (lines 211-219 in `docs/integration.md`) shows the consumer wiring WITHOUT `secrets: inherit`, mentions it only in the path-α section (where it's natural because the consumer is provisioning a token). A path-γ consumer following the quick-start lands on a shape the toolkit has never run. ## Proposed fix ### Docs (this tracker's scope) Update `docs/integration.md` quick-start to show `secrets: inherit` on the workflow_call for ALL consumers, with a short comment noting why: ```yaml jobs: release: uses: frankenbit/release-toolkit/.forgejo/workflows/_release.yml@vX.Y.Z secrets: inherit # toolkit references secrets.RELEASE_TOOLKIT_TOKEN # unconditionally; path-γ consumers still need the # reference to resolve — bash :- falls back to # GITHUB_TOKEN (ADR-0007 graceful-degradation) with: toolkit_ref: vX.Y.Z ... ``` Same edit applies to the consumer template at `.forgejo/workflows/release.yml` (toolkit-self's own consumer wrapper) — actually, toolkit-self's own wrapper ALREADY has `secrets: inherit`; the integration.md template is the under-specified one. ### Optional substrate hardening The unconditional `secrets.RELEASE_TOOLKIT_TOKEN` reference could be made conditional on the env-block level (only declare the env var when path-α conditions hold), eliminating the inherit requirement for path-γ consumers. This is a substrate change with its own design space; not blocking the docs fix. ## Test seam consideration A bats-level test for "consumer template includes `secrets: inherit`" could be added as a regression guard (similar to the `workflows.bats` schema checks). ## Refs - **Surfacing**: Surveyor dec5 review on cellblock#168 (10287cf) — first external consumer adoption surfaced this exact "docs-more-specific-to-toolkit-self" class catch - **The hot fix on cellblock**: cellblock#168 follow-up commit adds `secrets: inherit` to cellblock's release.yml inline - **Related substrate**: ADR-0007 (path-α token override + bash-level GITHUB_TOKEN fallback) - **Related substrate-state-care**: release-toolkit#89 (Forgejo's workflow_call secrets handling finickiness) - **Cross-repo coupling discipline**: this is the abstraction-boundary catch from the first external consumer — exactly the test value c28c framing predicted
Author
Owner

Empirical update 2026-06-26

The cellblock adoption PR (cellblock#168) was merged at 22:27:53 — BEFORE Surveyor's dec5 review surfaced this gap — so the without-secrets: inherit shape ran end-to-end as cellblock's first cut.

Empirical result: Forgejo's behavior on the missing secrets.RELEASE_TOOLKIT_TOKEN reference (no secrets: inherit on the consumer wrapper) is benign:

  • release-decide.sh ran cleanly (env block referenced the missing secret → resolved to empty → bash :- fell back to GITHUB_TOKEN)
  • draft-release.sh ran cleanly (same shape)
  • Rolling PR generated, operator merged, cut fired, v1.1.0 released (draft=false, path-γ manifest follow-up PR opened)

So this tracker's docs scope is non-blocking (the substrate works without inherit), but the gap is still real: a consumer reading integration.md's quick-start doesn't know the un-inherit shape is actually validated; the toolkit-self template HAS secrets: inherit; the inconsistency is real even if functionally moot.

Revised scope

  1. Docs fix: still warranted — show secrets: inherit in the quick-start consistently with what the toolkit-self does, with a note explaining the (now-validated) :- fallback as the substrate-honest mechanism
  2. Optional substrate hardening (making the secret reference conditional): deferred lower — the empirical evidence shows the unconditional reference is fine in practice
  3. Bats regression-guard for "consumer template includes secrets: inherit": still useful as a regression-guard for the docs decision

Banking

This tracker stays open for the docs fix; the substrate-hardening sub-scope becomes a "nice to have" rather than load-bearing. Surveyor's dec5 catch was correct CAUTION (don't gamble on a real consumer's first cut), and the empirical run vindicates the unconditional-reference shape as substrate-honest behavior.

Cellblock's first cut was the abstraction-boundary test exactly as Surveyor's c28c framing predicted — surfaced one real gap (these docs) + one non-blocking quirk (Ghost author on path-γ).

## Empirical update 2026-06-26 The cellblock adoption PR ([cellblock#168](https://git.frankenbit.de/frankenbit/cellblock/pulls/168)) was merged at 22:27:53 — BEFORE Surveyor's `dec5` review surfaced this gap — so the without-`secrets: inherit` shape ran end-to-end as cellblock's first cut. **Empirical result**: Forgejo's behavior on the missing `secrets.RELEASE_TOOLKIT_TOKEN` reference (no `secrets: inherit` on the consumer wrapper) is **benign**: - `release-decide.sh` ran cleanly (env block referenced the missing secret → resolved to empty → bash `:-` fell back to `GITHUB_TOKEN`) - `draft-release.sh` ran cleanly (same shape) - Rolling PR generated, operator merged, cut fired, v1.1.0 released (draft=false, path-γ manifest follow-up PR opened) So this tracker's docs scope is **non-blocking** (the substrate works without `inherit`), but the **gap is still real**: a consumer reading `integration.md`'s quick-start doesn't know the un-inherit shape is actually validated; the toolkit-self template HAS `secrets: inherit`; the inconsistency is real even if functionally moot. ### Revised scope 1. **Docs fix**: still warranted — show `secrets: inherit` in the quick-start consistently with what the toolkit-self does, with a note explaining the (now-validated) `:-` fallback as the substrate-honest mechanism 2. **Optional substrate hardening** (making the secret reference conditional): **deferred lower** — the empirical evidence shows the unconditional reference is fine in practice 3. **Bats regression-guard** for "consumer template includes `secrets: inherit`": still useful as a regression-guard for the docs decision ### Banking This tracker stays open for the docs fix; the substrate-hardening sub-scope becomes a "nice to have" rather than load-bearing. Surveyor's `dec5` catch was correct CAUTION (don't gamble on a real consumer's first cut), and the empirical run vindicates the unconditional-reference shape as substrate-honest behavior. Cellblock's first cut was the abstraction-boundary test exactly as Surveyor's c28c framing predicted — surfaced one real gap (these docs) + one non-blocking quirk (Ghost author on path-γ).
Author
Owner

Closing — implemented in PR #136 (test-seams + docs sprint).

  • docs/integration.md quick-start template now shows secrets: inherit consistently with toolkit-self's own consumer wrapper, with the substrate-honest justification (unconditional reference + bash :- fallback) inline
  • tests/workflows.bats gained a regression guard asserting the consumer wrapper has secrets: inherit on every workflow_call job — catches a future edit that would re-introduce the documented-vs-validated gap

Empirical update from the cellblock first-cut adoption (2026-06-26): the un-inherit shape ran end-to-end without failure (Forgejo resolved the missing reference benignly; bash ${VAR:-} fell back to GITHUB_TOKEN per ADR-0007). So the gap was docs-consistency rather than a substrate blocker; the docs fix closes the consumer-reading-the-docs confusion vector.

Close-keyword from #136 didn't fire; cleaning up the tracker now.

Closing — implemented in PR [#136](https://git.frankenbit.de/frankenbit/release-toolkit/pulls/136) (test-seams + docs sprint). - `docs/integration.md` quick-start template now shows `secrets: inherit` consistently with toolkit-self's own consumer wrapper, with the substrate-honest justification (unconditional reference + bash :- fallback) inline - `tests/workflows.bats` gained a regression guard asserting the consumer wrapper has `secrets: inherit` on every workflow_call job — catches a future edit that would re-introduce the documented-vs-validated gap Empirical update from the cellblock first-cut adoption (2026-06-26): the un-inherit shape ran end-to-end without failure (Forgejo resolved the missing reference benignly; bash `${VAR:-}` fell back to `GITHUB_TOKEN` per ADR-0007). So the gap was docs-consistency rather than a substrate blocker; the docs fix closes the consumer-reading-the-docs confusion vector. Close-keyword from #136 didn't fire; cleaning up the tracker now.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#135
No description provided.