Skip to content

feat: add issue-labels, issue-type, and auto-close inputs to link-check - #136

Merged
kyle-sexton merged 1 commit into
mainfrom
feat/link-check-issue-inputs
Jul 17, 2026
Merged

feat: add issue-labels, issue-type, and auto-close inputs to link-check#136
kyle-sexton merged 1 commit into
mainfrom
feat/link-check-issue-inputs

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

The link-check reusable hard-coded the rolling issue's labels, left the native issue type unset, and always closed on recovery — so a caller with an established issue scheme (medley's docs-link-check, EPIC melodic-software/medley#1491: Type=Task, area: guardrails / area: claude-code / automated, no auto-close) could not adopt it without a product-behavior change. This exposes those behaviors as workflow_call inputs whose defaults reproduce the current behavior exactly; existing callers need no changes.

  • issue-labels (default automated, link-check) feeds both the create/update step and the tracking-issue lookup. The lookup now filters on the full configured set — the list-issues API ANDs the comma-separated labels value, each label percent-encoded individually so label text (spaces, colons) cannot split or terminate the query — normalized exactly the way create-issue-from-file parses its labels input (split on commas/newlines, trim, drop empties). An empty set fails closed instead of silently dropping the same-owned-report constraint.
  • issue-type (default empty = skip) asserts a native issue type via gh issue edit --type after create/update, re-asserting on update the same way labels are re-applied.
  • auto-close (boolean, default true) gates the recovery close step; disabled leaves recovery triage to a human.

Medley's actual adoption is out of scope (happens after a release); no consumer pins are bumped and no release is cut here.

Verification

  • node --test .github/scripts/*.test.cjs: 199/199 locally (5 new/extended link-check assertions pin the defaults, shared label filter, fail-closed empty-labels path, type-step gating/ordering, and the auto-close gate).
  • actionlint (with embedded-script ShellCheck): clean. zizmor: no findings. Biome 2.5.4, typos, editorconfig-checker, markdownlint: clean.
  • Label normalization exercised empirically for the default set, medley's newline-separated set, and empty/whitespace inputs.
  • Live API check: issues?state=open&labels=area%3A%20guardrails,area%3A%20claude-code,automated returns exactly medley's real rolling issue (#1562), confirming AND semantics + encoding, and that the existing title-fallback adopts a pre-reusable issue on migration.

Related

  • melodic-software/medley#1491 (rolling-issue design this preserves)
  • melodic-software/github-iac#78 (Wave 3 deferred lanes)

Closes #132

🤖 Generated with Claude Code

https://claude.ai/code/session_01K3QehVwmWzkBLpKokNCkkt

Behavior-rich local callers (medley's docs-link-check, EPIC medley#1491)
maintain a rolling issue with their own label scheme, a native issue
Type, and no close-on-recovery; the reusable hard-coded all three, so
adopting it changed product behavior. Expose them as workflow_call
inputs whose defaults reproduce the current hard-coded behavior exactly,
so existing callers need no changes:

- issue-labels (default 'automated, link-check') feeds both the
  create/update step and the tracking-issue lookup: the lookup now
  filters on the full configured set (the list-issues API ANDs the
  comma-separated value; each label percent-encoded individually),
  normalized exactly the way create-issue-from-file parses its labels
  input. An empty set fails closed instead of silently dropping the
  same-owned-report constraint.
- issue-type (default '') asserts a native issue type via gh issue edit
  after create/update; empty skips the step.
- auto-close (default true) gates the recovery close step.

Conformance tests pin the defaults, the shared label filter, the
fail-closed empty-labels path, the type-step gating/ordering, and the
auto-close gate on the close condition.

Closes #132

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3QehVwmWzkBLpKokNCkkt
@claude

claude Bot commented Jul 17, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 56s —— View job


PR Review — feat: add issue-labels, issue-type, and auto-close inputs to link-check

  • Gather context and read changed files
  • Review link-check.yml workflow changes
  • Review link-check.test.cjs test changes
  • Review README.md changes
  • Check alignment with CLAUDE.md guidelines
  • Post final review

Overall: This is a clean, backward-compatible extension. The three new inputs are well-motivated, the defaults preserve existing callers exactly, the implementation is secure, and the test suite covers the new behaviors thoroughly. No blocking issues.


Security & Correctness

Label URL encoding (link-check.yml lines 154–161)

The jq @uri pipe is the right tool — each label is encoded individually before joining, so label text containing spaces, colons, or ampersands cannot split the labels=… query parameter or inject additional URL parameters. The fail-closed guard on an empty labels_query correctly preserves the same-owned-report constraint. Good.

Shell injection in the new Assert native issue type step (line 220)

ISSUE_TYPE is injected via env var, not via YAML expression interpolation into the run: string, so gh issue edit "$NUMBER" ... --type "$ISSUE_TYPE" is safe against shell injection. ✓

inputs.auto-close in the if: expression (line 223)

workflow_call boolean inputs evaluate to a native boolean in GHA expression contexts, so && inputs.auto-close works exactly as intended. ✓

steps.issue.outputs.issue-number availability (lines 214–220)

The "Assert native issue type" step guards on the same exit_code != '0' condition as the step it depends on, so steps.issue.outputs.issue-number is always populated when this step can run. If "Open or update tracking issue" fails, subsequent steps are skipped by default. ✓

CLAUDE.md compliance

All uses: remain SHA-pinned with version comments. Permissions are unchanged (contents: read, issues: write). No new event triggers. ✓


One Minor Observation

The run: in "Assert native issue type" has no explicit shell: bash. GitHub's default on Linux is bash --noprofile --norc -eo pipefail, which provides -e (exit on error) — fine for a single-command step. There's no need for a shell: bash declaration or set -euo pipefail here, but if this step ever grows more logic, keep the pattern used in the rest of the workflow.


Test Coverage

The five new assertions cover exactly the right behaviors:

Test What it pins
default to established rolling-issue behavior All three new inputs have backward-compat defaults
tracking lookup filters on the same labels Label set flows from input → lookup → update step (consistency)
Empty labels_query Fail-closed path
@uri encoding Per-label percent-encode so label text can't split the query
native issue type is asserted after create or update Ordering, guard condition, output reference

The inputBlock regex (\\n {6}${name}:\\n(?: {8}.*\\n)+) correctly matches the YAML indentation in the workflow_call.inputs block. ✓

The update-step slice in the label-consistency test (workflow.slice(updateIndex, typeIndex)) relies on the step ordering introduced by this PR, which is itself validated by separate ordering assertions — intentional and correct.


README

The README update accurately reflects that close-on-recovery is now conditional ("by default, closing it after the next clean run") and lists all four configurable behaviors. Concise and correct.


Verdict: Ready to merge. No issues requiring changes.

@kyle-sexton
kyle-sexton merged commit 71e576c into main Jul 17, 2026
35 checks passed
@kyle-sexton
kyle-sexton deleted the feat/link-check-issue-inputs branch July 17, 2026 19:18
kyle-sexton added a commit to melodic-software/standards that referenced this pull request Jul 17, 2026
No linked issue.

Registers `ec91c3433a8c3c0a7ebbdd239286e5a6a25eeec5` (ci-workflows
v0.6.0, containing ci-workflows#135/#136/#137) so the consumer pin wave
can proceed. Selector-lockstep step 2; the consumer repin PRs (step 3)
follow after this syncs.

- **Selector** — appended to the `melodic-software` owner scope. The
revision admits `merge_group` and `pull_request_target` for
metadata-only required gates (`merge_group` has no fork variant and only
write-access users can enqueue one; `pull_request_target` executes the
trusted base-ref definition) and extends the fork guard to both
pull-request event names, so fork-origin PR contexts still route off the
managed fleet. README review note added; approved-revision counts
updated (nine total, six strict-scheduling).
- **claude-review contract @ec91c343** — mirrors the registered
`df54d0e` runner-input shape exactly (`runner` + `skip-actors`,
`CLAUDE_CODE_OAUTH_TOKEN`, same `allowedCallerPermissions` waiver). The
interface is unchanged since `df54d0e`; the revision's delta is
behavioral only (per-(PR, head-SHA) concurrency keying + superseded-head
guard). The `standards-ref` mount input and `STANDARDS_REVIEW_APP_*`
secrets stay unregistered — no consumer passes them, and
grants/allowances are never provisioned ahead of need.
- **link-check contract @ec91c343** — carries the `3dfb1845`
runner-input shape forward (`runner`, `args`, `contents: read` +
`issues: write` waiver) and admits the revision's new rolling-issue
interface (`issue-title`, `issue-labels`, `issue-type`, `auto-close`),
which the medley docs-link-check adoption consumes next.
- **semantic-pr, do-not-merge-gate, pr-issue-linkage contracts
@ec91c343** — selector-result contracts decline the structural
auto-approval unconditionally (fail-closed required-check behavior is
unobservable to the surface diff), so uniform pins need reviewed
entries. pr-issue-linkage is byte-identical to `f7e94a80`; the other two
changed only `prerequisite-result` description text. Shapes carried
forward verbatim.
- **pulumi-version-drift-check contract @ec91c343** — carries
`allowedCallerPermissions`, which declines auto-approval
unconditionally; byte-identical to the reviewed `3dfb1845` revision.
Shape carried forward verbatim.
- **pester contract @ec91c343** — input-declaration surface changed
(`pester-version` default 5.7.1 → 6.0.0 plus an import version
assertion), so auto-approval correctly refuses; hosted-only
`windows-2025` shape carried forward verbatim.
- **zizmor / osv-scanner** — intentionally NOT registered at `ec91c343`:
both are byte-identical to reviewed revisions with no unobservable-trust
terms, so the deterministic auto-approval extension covers their pin
bumps.

## Verification

- `GITHUB_REPOSITORY=melodic-software/standards npm run
lint:runner-policy` — passed
- `npm run test:runner-policy` — 208/208 (selector-allowlist snapshot
extended)
- `npm run lint:md` — 0 errors

## Related

- melodic-software/github-iac#78 (epic — Campaign A hosted-floor
elimination)
- melodic-software/ci-workflows#135, melodic-software/ci-workflows#136,
melodic-software/ci-workflows#137 (v0.6.0 content)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

link-check: add issue-title, labels, and issue-type inputs so behavior-rich local callers can adopt it

1 participant