Skip to content

feat: ADDON-90824 add RFCT CIM changes workflow job - #547

Open
mbruzda-splunk wants to merge 8 commits into
developfrom
feat/addon-90824-rfct-cim-changes
Open

mbruzda-splunk wants to merge 8 commits into
developfrom
feat/addon-90824-rfct-cim-changes

Conversation

@mbruzda-splunk

Copy link
Copy Markdown
Contributor

Description

Adds an opt-in RFCT (Regression Field Comparison Tool) CIM changes capability to the reusable
release workflow, so an add-on that changes its CIM mappings can compare the candidate package
against a released baseline and produce versioned CIM changes documentation as a run artifact.

Ticket: ADDON-90824.
Baseline: develop at e884eeb2a88a13245dffc6aa67a5044064dc9a13.

What this adds

  • plan-rfct job + .github/actions/plan-rfct — event and changed-path routing, input validation,
    and immutable baseline/candidate selection. Skips fork PRs and pull_request_target before any
    credential is used. Freezes the chosen revisions so a release published mid-run cannot change the
    comparison. Emits rfct-plan.json.
  • run-rfct job — acquires both packages, verifies app identity across them, records checksums,
    authenticates to ECR, resolves the image to a digest, checks formatter compatibility, invokes the
    action, uploads evidence, and tears down even after failure.
  • .github/actions/run-rfct — a narrow composite action that validates prepared inputs and image
    capabilities, runs compare report then format-report docs-markdown, writes execution.json,
    and cleans up its own containers. It receives no cloud or repository credentials, does not
    provision environments, and does not fetch packages.
  • Four optional workflow_call inputs (rfct-enabled default false, rfct-image,
    rfct-baseline-release, rfct-candidate-release) and two optional secrets
    (RFCT_ECR_AWS_ACCESS_KEY_ID, RFCT_ECR_AWS_SECRET_ACCESS_KEY).
  • Additive build outputs package-version and source-sha, so RFCT never guesses the candidate
    version from a package filename.

Existing callers are unaffected: with rfct-enabled at its false default, neither new job appears
in a run.

Open decision — this job cannot run yet

The runtime backend for provisioning two paired Splunk environments is unresolved (existing WFE
infrastructure, another supported remote backend, or runner-local Docker). The WFE path used by
run-knowledge-tests submits an Argo workflow that runs the tests itself and returns results via
S3; it exposes no supported contract for two live Splunk endpoints held open across a comparison,
so it is not reusable as is.

Both environment steps are therefore stubs marked TODO: backend TBD (ADDON-90824), each
documenting the contract its replacement must satisfy (inputs, installation and verification,
independent-parsing proof, credential masking, config output, lease expiry, idempotent teardown).
Preparation fails with backend-not-selected rather than reporting a comparison that did not
happen, and the action is gated on prepared == 'true'. Everything that does not depend on the
backend is implemented and tested.

Deliberate decisions worth reviewing

  • Advisory, not blocking. run-rfct is not wired into pre-publish, because the publication
    policy is still open. The README records how to add it for opted-in comparisons if blocking is
    later agreed.
  • Trusted action source. Both actions are referenced as
    splunk/addonfactory-workflow-addon-release/.github/actions/...@6f80fae, not a relative
    ./.github/actions path, which would resolve inside the add-on checkout and execute
    add-on-controlled code. The second commit pins the exact commit that introduced the actions. The
    release handoff replaces this with the released workflow tag.
  • No expression interpolation in shell bodies. Changed paths come from the add-on's own diff and
    the app code name is derived from an untrusted package archive, so every value reaches a run:
    body through env:. A test asserts no ${{ appears in any RFCT step's run: body.
  • beta.7 is rejected on purpose. The rfct-image default is the verified beta.7 digest, whose
    formatter predates the Table 1/Table 2 implementation and lacks --ta-repo-path. The
    compatibility preflight runs before provisioning (and again inside the action, protecting a direct
    caller) and rejects it, so beta.7 Markdown can never be uploaded as a conforming result. No
    version is hardcoded as the first stable release; semantic-release determines it, and v1.0.0
    and v1.0.1 already exist in RFCT history.
  • Missing output never means no change. The outcome is read from validated RFCT output; an
    absent, malformed, or outcome-less report is an invalid-report failure.
  • check-docs-changes left alone. Its diff range is PR-shaped and is not a valid range for
    every push or dispatch, as the design doc notes. Fixing it changes routing for every existing job
    and every consumer, so it is not bundled here; RFCT is already insulated from it (plan-rfct has
    no dependency on it, and run-rfct only needs build when comparing a build candidate). Worth
    a separate ticket.
  • The RFCT CLI flag names for compare report / format-report docs-markdown follow the
    documented contract but are unverifiable against beta.7; they are centralized in
    compare_argv / format_argv with a comment to re-confirm them against the first stable image.

Security-relevant behavior

  • Fork PRs and pull_request_target skip before credentials are requested.
  • Registry credentials are removed (docker logout, docker config deleted, AWS env vars cleared)
    before any add-on code, build hook, or sample could run.
  • Checkouts use persist-credentials: false; the candidate source is exported without .git.
  • Only the config (read-only), candidate source (read-only), and output directory are mounted. No
    Docker socket, no home directory, no AWS or GitHub credentials enter the container.
  • The live RFCT configuration is never uploaded; the artifact allowlist is explicit. Diagnostics are
    redacted for credential-shaped values.
  • Package archives are treated as untrusted data: entry names are listed, never extracted.
  • continue-on-error is not used, so an execution failure cannot become a successful comparison.

Checklist

  • README.md has been updated or is not required
  • push trigger tests
  • manual release test
  • automated releases test
  • pull request trigger tests
  • schedule trigger tests
  • workflow errors/warnings reviewed and addressed

Testing done

Local required checks, on d92cc6f:

  • poetry run pytest — 126 passed, total coverage 91.65% (gate is 80%).
    New: tests/test_plan_rfct.py (46), tests/test_run_rfct.py (53).
  • pre-commit run --all-files — yamlfmt, actionlint, Ruff C901, and the workflow
    unused-input/secret hygiene check all pass.

Covered by unit tests: supported-path matching including additions, deletions and both sides of
renames; paginated compare and release walks plus the pagination bound; event-aware diff ranges
including a newly created branch with an absent before and missing history; release ordering,
draft/prerelease exclusion, and a baseline that is the built revision; app identity and
exactly-one-.spl enforcement; invalid and injection-shaped release references; fork skip asserted
to make no API call; fixture-only deterministic rendering; no-change result; malformed JSON;
non-object and outcome-less reports; formatter preflight rejection with comparison proven not to
run; failed-phase evidence and exit-status retention; invocation cleanup on success and failure;
local-path, symlink, .git, and digest-pinning validation; output mapping; and workflow-structure
assertions for the required-job skip, digest-lookup guard, credential-removal ordering, upload and
teardown on failure, and pre-publish remaining ungated.

Not done — E2E validation is outstanding and is a blocker for feature acceptance. No
splunk/test-addonfactory-repo run is recorded, because run-rfct cannot complete until the
runtime backend is selected. The remaining checklist boxes stay unticked for that reason. Once a
backend lands, the E2E evidence required by AGENTS.md (workflow SHA, template caller branch and
commit, TA repo/head SHA/event/base branch/run URL, expected jobs, artifacts, cleanup) must be
recorded before release, along with a mapping-change PR, a docs-only skip, trusted develop and
release-branch pushes, a fork skip, and a dispatch with explicit released versions. The template
caller in addonfactory-repository-template also still needs the matching inputs, optional
secrets, and dispatch fields.

🤖 Generated with Claude Code

mbruzda-splunk and others added 2 commits November 2, 2026 18:08
Opted-in add-ons can plan and run a Regression Field Comparison Tool
comparison between the candidate package and a released baseline, so CIM
mapping changes are documented from validated tool output instead of being
reviewed by hand.

plan-rfct applies event and changed-path routing, skips fork pull requests
before any credential is used, and freezes the baseline and candidate
revisions so a release published mid-run cannot change the comparison.
run-rfct acquires both packages, pins the RFCT image by digest, and invokes a
narrow composite action that holds no cloud or repository credentials.

The runtime backend for provisioning two paired Splunk environments is still
open, so both environment steps are stubs marked TODO: backend TBD and
document the contract their replacement must satisfy. Preparation fails with
backend-not-selected rather than reporting a comparison that did not happen.
The job is advisory and is deliberately not wired into pre-publish while the
publication policy is undecided.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A relative ./.github/actions path would resolve inside the add-on checkout and
run add-on-controlled code, and a branch ref is mutable. Pin both references to
the commit that introduced the actions so validation runs are reproducible. The
release handoff replaces these with the released workflow tag.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@mbruzda-splunk
mbruzda-splunk requested a review from a team as a code owner November 2, 2026 17:10
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-05T14:21:03.106115Z e49f9ba New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Dry-running the planner against splunk/test-addonfactory-repo surfaced two
cases that would have failed the first comparison on the shared fixture.

A release publishes the splunkbase package alongside partitioned deployment
packages, so v0.8.7 carries both Splunk_TA_box-0.8.7.spl and
Splunk_TA_box-0.8.7-_search_heads.spl. Requiring exactly one .spl asset
rejected every such release. Identify the package by its version-suffixed
name instead, keeping the single-asset fallback for unversioned naming.

The fixture also carries a bare 1.0.0 tag with no assets, which outranks every
release that does publish a package. Automatic baseline selection now falls
through to the next older release instead of failing a run that never asked to
be pinned there. An explicitly pinned baseline without a package stays an input
failure.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d92cc6fe67

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

f"{quote(base, safe='')}...{quote(head, safe='')}"
"?per_page={per_page}&page={page}"
)
files = paginate(url_template, token, lambda payload: payload.get("files") or [])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle the compare API's 300-file limit

For comparisons touching more than 300 files, this pagination cannot retrieve the remainder: GitHub's compare endpoint documentation states that changed files appear only on the first page and are capped at 300 for the entire comparison. If a supported mapping path falls after that cap, paths can be empty and the planner incorrectly records no-mapping-changes; use an uncapped enumeration method or conservatively proceed whenever the response reaches the limit.

Useful? React with 👍 / 👎.

Comment on lines +137 to +140
commit = (payload.get("object") or {}).get("sha")
if not commit:
raise PlanInputError(f"Could not resolve a commit for tag {tag}")
return commit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Dereference annotated release tags to commits

When a release uses an annotated tag, the ref object's SHA is the tag-object SHA rather than the underlying commit SHA; GitHub's Git tag documentation shows that the commit is in the tag object's nested object.sha. Returning the ref SHA here records incorrect provenance and makes a manual release candidate's later actions/checkout attempt to check out a non-tree object, so inspect the ref object's type and recursively dereference tag objects to a commit.

Useful? React with 👍 / 👎.


# The archive is untrusted data: list entry names only, never extract.
app_name_of() {
tar -tzf "$1" | head -n 1 | cut -d/ -f1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid SIGPIPE while reading the archive root

For packages whose tar -t output exceeds the pipe buffer, head closes the pipe after the first entry and GNU tar exits with status 141; because this step enables set -euo pipefail, the baseline_app=$(app_name_of ...) or candidate assignment then aborts package verification even for a valid .spl archive. Read the first member without a short-circuiting pipeline, or explicitly tolerate tar's SIGPIPE while still validating actual archive errors.

Useful? React with 👍 / 👎.

Comment on lines +30 to +33
SECRET_RE = re.compile(
r"(?i)\b(pass(?:word|wd)?|token|secret|api[_-]?key|authorization)\b"
r"(\s*[:=]\s*|\s+)(\S+)"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact complete authorization and underscored secret values

When RFCT stdout or stderr contains common forms such as Authorization: Bearer xyz, hec_token=xyz, access_token=xyz, or splunk_password=xyz, this regex leaves the credential exposed: it replaces only Bearer in the authorization example, and the word boundary prevents matching names joined with underscores. Because run_phase writes this sanitized text into uploaded log artifacts, an RFCT error that prints one of these standard credential forms leaks the temporary endpoint credential; redact the full authorization value and support credential-key prefixes and separators.

Useful? React with 👍 / 👎.

Comment on lines +4166 to +4170
status="${ACTION_STATUS:-failed}"
reason=""
if [ -z "${ACTION_STATUS:-}" ]; then
reason="${PREPARE_REASON:-execution-incomplete}"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the actual failure reason in the result manifest

When image preflight, package acquisition, registry setup, or another step before preparation fails, both inputs here are empty and the manifest records only execution-incomplete; when the RFCT action itself emits ACTION_STATUS=failed, reason remains empty even though execution.json contains the specific failure reason. This defeats the documented machine-readable provenance for precisely the failures the artifact is intended to diagnose, so derive the reason from the failed phase or copy .reason from execution.json when it exists.

Useful? React with 👍 / 👎.

Comment on lines +329 to +332
if baseline_parsed["order"] >= candidate["order"]:
raise PlanInputError(
f"Baseline {baseline['tag']} must be older than candidate {candidate['tag']}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare prerelease identifiers when ordering releases

When a manual comparison explicitly selects prereleases with the same major, minor, and patch—for example baseline v2.0.0-beta.1 and candidate v2.0.0-beta.2—both are accepted by parsing and release lookup, but their order tuples are identical because the prerelease component was discarded. This check therefore rejects the genuinely older baseline as >= the candidate; either implement SemVer prerelease ordering or reject prerelease overrides consistently instead of accepting combinations that cannot be ordered.

Useful? React with 👍 / 👎.

The first fixture run failed with the planner error message from the commit
before the package-selection fix: the workflow still pinned the actions to that
older commit, so CI loaded superseded helper code while the workflow itself was
current. Pinning to an immutable commit means every action edit needs a matching
re-pin, which is easy to miss.

Re-pin both references to the commit that last changed the action directories,
and add a test that compares the pinned ref against that commit so a stale pin
fails locally instead of in a fixture run. The test skips when git history is
unavailable, as in a shallow checkout.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2eb5e4ebe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +23 to +24
r"\A\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com/ta-automation/rfct"
r"@sha256:[0-9a-f]{64}\Z"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict images to the approved ECR registry

When the composite action is called directly, an image such as 123456789012.dkr.ecr.eu-west-1.amazonaws.com/ta-automation/rfct@sha256:... passes this regex even though the documented direct-call contract in README.md:1266 requires account 956110764581 in us-west-2. The comparison container is then given network access and a read-only mount of the configuration containing temporary endpoint credentials, so a mistaken or user-controlled override can run an unapproved image with those credentials; match the exact approved registry as the reusable-workflow wrapper already does.

Useful? React with 👍 / 👎.

Comment on lines +332 to +334
status, candidate_version = read_outcome(report_path)
execution["status"] = status
execution["candidate_version"] = candidate_version

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail when the formatter produces no documentation

When a compatible formatter exits zero but emits no Markdown—for example because it accepts the required flag but cannot render the report schema—this immediately derives a successful changes or no-changes status solely from comparison/report.json and never inspects the docs directory. The artifact can therefore be reported as successful without the promised versioned page (including the explicitly required no-change page); verify that the expected documentation output exists before setting the success status.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

token: ${{ github.token }}
event-name: ${{ github.event_name }}
base-branch: ${{ github.event_name == 'pull_request' && github.base_ref || github.ref_name }}
is-fork: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork || 'false' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Determine fork PRs by repository identity

For a same-repository PR in an add-on repository that is itself a fork, this evaluates to true because GitHub's repository-level isFork field identifies whether the repository itself is a fork, not whether this PR crosses repository boundaries. The planner consequently records fork-pull-request and skips a same-repository route that the public workflow contract says should run; compare head.repo.full_name with github.repository instead.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

The fixture run reached package verification and failed with exit code 2 and
"tar: stdout: write error". Piping tar -tzf into head closes the pipe after the
first line, GNU tar reports a write error, and pipefail turns that into a step
failure. It would have broken every add-on, not just the fixture.

Capture the listing into a variable and slice it with parameter expansion, and
send the package-count error to stderr so it is not swallowed by the command
substitution that calls it.

bsdtar exits 0 in the same situation, so a behavioural test cannot reproduce
this off Linux. Guard it with a platform-independent check that the step never
pipes an archive listing anywhere, and add a test that executes the step against
built packages to cover app-identity resolution, checksums, mismatch, and an
ambiguous package set.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@mbruzda-splunk

Copy link
Copy Markdown
Contributor Author

E2E validation evidence

Validated against the shared fixture splunk/test-addonfactory-repo#392
(temporary branch test/addon-90824-rfct-plan, PR into fixture develop, event pull_request).

Item Value
Workflow commits exercised 87fd481 → e2eb5e4 → 7f5841a (three runs)
Fixture caller temporarily @<workflow-sha> with rfct-enabled: true; develop itself untouched at @v5.6
Fixture TA / head SHA Splunk_TA_box, head 845d28f9, merge 9134c5f0
Final run 41493003944
Runner ubuntu-latest, Splunk 10.6.0, SC4S 3.40.0 from meta

Two real bugs found by running it

Both were invisible to the unit suite and to local dry runs.

1. Stale action pin — run 41490523870
failed with Release v1.0.0 must publish exactly one .spl asset, found 0, the error text from before
the package-selection fix. The workflow was current but still pinned its actions to the preceding
commit, so CI loaded a superseded planner. Pinning to an immutable SHA means every action edit needs
a matching re-pin and nothing enforced that. Fixed in e2eb5e4, which re-pins and adds a test
comparing the pinned ref against git log -1 -- .github/actions/….

2. tar write error under pipefail — run 41491067965
failed at package verification with exit code 2 and tar: stdout: write error. tar -tzf | head -n 1
closes the pipe after one line; GNU tar reports a write error and pipefail propagates it. This would
have broken every add-on. Fixed in 7f5841a by capturing the listing and slicing it with parameter
expansion. The package-count error also now goes to stderr; it was being swallowed by the command
substitution that called it.

Worth recording: bsdtar exits 0 in the same situation (verified up to a 282 KB listing), so bug 2 is
not reproducible off Linux. The guard that pins it is a platform-independent check that the step never
pipes an archive listing; the behavioural test that executes the step covers identity, checksums,
mismatch, and ambiguous package sets.

plan-rfct — confirmed correct

From artifact rfct-plan-41491067965-1:

{ "decision": "proceed", "reason": "mapping-change",
  "changed_paths": ["package/default/props.conf"],
  "baseline": { "tag": "v0.8.7", "asset_name": "Splunk_TA_box-0.8.7.spl",
                "commit": "2290aa46709754336f43761075f2ac05f48edb91" },
  "candidate": { "source": "build" },
  "build_sha": "bdf0ba8986a3...", "pull_request_head_sha": "3ff14a40a6f2..." }

Proven on live data: PR-to-develop routing; path matching that selected props.conf while ignoring
the caller-workflow change in the same diff; baseline selection skipping the asset-less 1.0.0 tag and
choosing the splunkbase package over -_search_heads.spl; merge commit recorded distinctly from the PR
head; plan artifact and step summary produced; planner loaded from the pinned commit.

run-rfct — reaches the expected blocker

Final run step outcomes:

2  success  Resolve RFCT working paths
3  success  Initialize RFCT result manifest
4  success  Download candidate package from this run
6  success  Download baseline package
7  success  Verify package identity and record checksums
8  success  Checkout candidate source for formatter attribution
9  success  Export candidate source without history
10 success  Select the Splunk version used on both sides
11 FAILURE  Configure AWS credentials for the RFCT registry
12-17 skipped (ECR login, image pull, preflight, credential removal, prepare, action)
18 success  Assemble RFCT result manifest
19 success  Upload RFCT evidence
20 success  Summarize RFCT comparison
21 success  Tear down paired Splunk environments
22 success  Remove temporary RFCT credentials and configuration

Credentials could not be loaded, please check your action inputs — expected:
RFCT_ECR_AWS_ACCESS_KEY_ID / RFCT_ECR_AWS_SECRET_ACCESS_KEY exist in neither the fixture repository
nor its organization. The read-only ECR identity for ta-automation/rfct has not been provisioned.

This also exercised the evidence-on-failure contract for real. Uploaded result.json:

{ "schema_version": 1, "status": "failed", "reason": "execution-incomplete",
  "app_code_name": "Splunk_TA_box",
  "baseline": { "tag": "v0.8.7", "package_sha256": "73a58ed3c504..." },
  "candidate": { "source": "build", "version": "0.392.41493003944",
                 "commit": "9134c5f0...", "package_sha256": "a7411964ad2b..." },
  "splunk_version": "10.6.0", "sc4s_version": "3.40.0",
  "changed_paths": ["package/default/props.conf"],
  "rfct_image_digest": "", "phases": {}, "produced": ["result.json"] }

App identity resolved from the real archive, both package checksums recorded, the synthetic PR version
taken from the new additive build output rather than guessed from a filename, merge and head SHAs kept
apart, and no credential material present. phases is empty and the image digest blank because the
action never ran — correct.

Not yet proven

  • Image acquisition, formatter preflight, and digest resolution — blocked on the ECR credentials
    above. The preflight is expected to reject beta.7; that has not been demonstrated on CI.
  • Provisioning, ingestion, comparison, formatting — blocked on the runtime backend decision.
    prepare-environments fails with backend-not-selected by design.
  • Rough edge: a failure before preparation reports reason: execution-incomplete, which is accurate but
    unspecific. Worth a more precise reason once the credential and backend paths are real.

Pre-existing, unrelated failures

fossa-license-test and fossa-vulnerability-test failed in every run, including before any RFCT step
executed, and are not in build's dependencies. The fixture was already red: scheduled runs on its
main have been failing hourly and its main caller points at a stale @agent/manual-publish-approval
branch. Neither is attributable to this change.

Cleanup

Fixture PR #392 and branch test/addon-90824-rfct-plan are still open pending review of this evidence,
then both will be removed. Fixture develop was never modified, so its caller remains at @v5.6 and
needs no revert.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f5841ab0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

options = baseline_candidates(releases, explicit, candidate)
for release, parsed in options:
described = describe_release(repository, token, release, parsed)
if explicit or not build_sha or described["commit"] != build_sha:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject an explicit baseline at the build revision

When a build candidate explicitly selects a baseline release whose tag resolves to build_sha—for example, a manual run from the commit currently tagged as that release—the explicit or branch accepts it, and the later equality check is only performed for release candidates. The workflow consequently compares two packages built from the same revision instead of rejecting or skipping the candidate revision as required by the public baseline-selection contract; apply the commit-equality check to explicit build baselines as well.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

"""Create the evidence tree writable by the image's default UID 10001."""
for relative in ("", "comparison", DOCS_RELATIVE, "logs"):
directory = output_directory / relative if relative else output_directory
directory.mkdir(parents=True, exist_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale RFCT outputs before running phases

When a direct caller reuses an existing output directory, exist_ok=True preserves any prior comparison/report.json and generated docs. If the current compare or formatter invocation exits zero without replacing those files, read_outcome consumes the stale report and the action can report the previous run's changes or no-changes result rather than detecting missing current output; remove the known generated paths or require an empty output directory before execution.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

Comment on lines +107 to +111
output_directory = Path(_input("OUTPUT_DIRECTORY") or "")
if not output_directory.is_absolute():
raise ExecutionError("invalid-input", "output-directory must be an absolute path")
if output_directory.is_symlink():
raise ExecutionError("invalid-input", "output-directory must not be a symbolic link")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the credential config outside the artifact directory

When a direct caller places config-path underneath output-directory, both paths pass validation, but the documented upload of the returned artifact directory then includes the live RFCT configuration and its temporary endpoint credentials. The output bind also exposes the same file through a writable container path despite its separate read-only mount, so reject configurations contained by the output directory before execution.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

@mbruzda-splunk

Copy link
Copy Markdown
Contributor Author

Correction: a third pre-existing failure, and what it illustrates

My evidence comment listed only the two FOSSA checks as pre-existing. A fourth check,
call-workflow / validate-docs-change, also fails on the fixture PR. It is likewise not caused by this
change, but the mechanism is worth recording because it argues for one of the decisions here.

The fixture carries two documentation callers that both trigger on pull_request:

File Reusable workflow ref Result
.github/workflows/validate-deploy-docs.yaml reusable-validate-deploy-docs.yml@v5.2 success
.github/workflows/validate-deploy-docs.yml reusable-validate-deploy-docs.yml@feat/docs-reusable-workflow failure

Two runs therefore execute against the same commit and one always fails. The failing one logs
Docs validation success and then exits 1, because the revision on that feature branch has an
exit 1 in the mkdocs build --strict success path. reusable-validate-deploy-docs.yml on
develop does not have that bug, and this branch does not touch the file:

$ git diff --stat origin/develop...HEAD -- .github/workflows/reusable-validate-deploy-docs.yml
(no output)

So: a caller pinned to a mutable feature branch is still serving superseded, broken code long after
that branch stopped being current. That is precisely the failure mode the trusted-source requirement is
meant to prevent, and why both RFCT actions are pinned to an immutable commit rather than to
feat/addon-90824-rfct-cim-changes. The stale-pin guard added in e2eb5e4 covers the opposite hazard —
a pin that is immutable but out of date.

Pre-existing fixture failures in full, none attributable to this change: fossa-license-test,
fossa-vulnerability-test, and validate-docs-change (duplicate caller on a stale ref). The fixture
also has duplicated stale.yaml / stale.yml files, and its main caller points at
@agent/manual-publish-approval. Fixture PR
#390 ("use an existing shared workflow ref")
appears to target the same class of problem. Cleaning that up is fixture maintenance, outside this PR.

Two tests added with the previous fixes passed locally and failed on CI. Both
were test defects rather than product defects.

The package verification test built baseline and candidate archives from
identical content, which GNU tar renders byte for byte identically, so asserting
the checksums differ compared a value with itself. Give each side a distinct
payload.

The pinned-action staleness test read git history that a shallow checkout does
not have: git returned the pull-request merge commit instead of nothing, so the
skip guard never fired and the comparison failed. Skip when the repository is
shallow or the pinned commit is absent, and give this repository's own
pre-commit job full history so the check actually runs there instead of
silently skipping.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@mbruzda-splunk

Copy link
Copy Markdown
Contributor Author

Fixture cleanup complete

Per AGENTS.md, linking the cleanup into the validation evidence.

No caller reference needed restoring. The validation never modified fixture develop: it is still at
69b96f92 with reusable-build-test-release.yml@v5.6, verified after deletion. The temporary SHA
reference and the rfct-enabled: true opt-in existed only on the deleted branch.

This repository's own CI is green on ccc6666: 137 tests passing at 91.80% coverage, ci-gate passing.
The pinned-action staleness guard executes on CI rather than skipping, confirmed by zero skipped tests
in the run log.

Reminder of what remains before feature acceptance, unchanged by this cleanup: the read-only ECR
identity for ta-automation/rfct must be provisioned before image acquisition and the beta.7 formatter
rejection can be demonstrated, and the runtime backend decision must land before any comparison can run.
The template caller in addonfactory-repository-template still needs the matching inputs, optional
secrets, and dispatch fields, and both pinned action references must move to the released workflow tag at
release handoff.

mbruzda-splunk and others added 2 commits November 5, 2026 15:12
The default was the 1.0.0-beta.7 digest, whose formatter the compatibility
preflight rejects, so an opted-in caller could not produce a conforming CIM
changes result without overriding the input. RFCT 1.1.0 is published and its
formatter accepts --ta-repo-path, confirming the compare and format flag
names the action already relied on.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The image default change edited run_rfct.py, so the previous pin would have
run the superseded helper.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e49f9baed5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

raise PlanInputError("A pull request requires both a base SHA and a build SHA")
return base, head

after = _input("PUSH_AFTER") or _input("BUILD_SHA")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip branch-deletion push payloads

For an opted-in caller triggered when a release/* branch is deleted, GitHub still emits a push event, with after set to the all-zero SHA. This accepts that sentinel as the comparison head, so changed_paths requests compare/<before>...000..., receives a not-found response, and fails plan-rfct during routine branch cleanup instead of recording a skip; reject an absent after SHA or pass and check the payload's deleted field.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

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.

1 participant