Skip to content

ci(autodoc): route Claude execution through ai-workflows to fix Bedrock OIDC - #36842

Merged
jdcmsd merged 19 commits into
mainfrom
autodoc-action-addition
Aug 7, 2026
Merged

ci(autodoc): route Claude execution through ai-workflows to fix Bedrock OIDC#36842
jdcmsd merged 19 commits into
mainfrom
autodoc-action-addition

Conversation

@jdcmsd

@jdcmsd jdcmsd commented Jul 31, 2026

Copy link
Copy Markdown
Member

relates to: dotCMS/ai-workflows#65

Summary

  • Restructures the autodoc job into prep → claude → finalize so Claude/Bedrock runs inside dotCMS/ai-workflows/autodoc-executor.yml
  • This sets job_workflow_ref to the ai-workflows ref, satisfying the trust condition on GitHubActions-BedrockCodeReview that was blocking OIDC assumption with Not authorized to perform sts:AssumeRoleWithWebIdentity
  • prep and finalize jobs run directly in this workflow and do not touch AWS
  • Report path standardised to /tmp/autodoc-report.md; passed between jobs via Actions artifact

Root cause

The IAM role's trust policy requires job_workflow_ref to match dotCMS/ai-workflows/.github/workflows/*@refs/tags/*. When the Claude step ran directly inside issue_autodoc.yml, job_workflow_ref was dotCMS/core/.github/workflows/issue_autodoc.yml@... — a hard deny. Routing through the ai-workflows reusable workflow fixes the claim.

Dependency

Merge dotCMS/ai-workflows#65 and tag v3.5.0 before merging this PR. The claude job references autodoc-executor.yml@v3.5.0.

Test plan

  • Confirm dotCMS/ai-workflows#65 is merged and tagged v3.5.0
  • Merge this PR
  • Close a test Epic and confirm: OIDC succeeds, Claude runs, artifact uploaded, comment posted on issue, dotCMS draft saved

🤖 Generated with Claude Code

This PR fixes: #36949

@jdcmsd
jdcmsd requested a review from a team as a code owner July 31, 2026 15:45
@jdcmsd
jdcmsd marked this pull request as draft July 31, 2026 15:51

@sfreudenthaler sfreudenthaler left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These look good overall — nice work routing through ai-workflows to fix the OIDC trust issue, and the finalize step follows the same draft-then-apply pattern we landed for the changelog site publisher. Two things before this is ready to merge:

  1. No human-edit guardrail on the update path (see inline comment below). My changelog publisher checks modUser on the existing content and skips (with a visible skip marker) if the last edit wasn't made by the service account, specifically so the automation never clobbers a manual edit made in the dotCMS backend. This workflow's "Apply doc update to dotCMS" step has no equivalent check — it will overwrite the documentation field unconditionally, and there's no log line or alert anywhere that would tell us it happened. Given Jamie (or anyone) could be hand-editing these pages between Epic closures, I'd want that guardrail before this runs against real content.

  2. Publish-gate question (also inline below): my changelog automation auto-publishes with no approval gate, but that's safe because the content is deterministic — the GitHub release body IS the site content, mechanically transformed. Here Claude is generating original documentation prose, so there's real hallucination risk. Before this merges I want to confirm what AUTODOC_DOTCMS_BASE_URL points at and what the default EDIT/NEW workflow actions actually do on the DotcmsDocumentation content type — does firing them here land the page in Draft/Review, or does it go straight to Published with no human in the loop? If it's the latter, we need either a review gate before publish, or to restrict this to a staging/review instance until there's human sign-off on the draft.

Comment thread .github/workflows/issue_autodoc.yml
Comment thread .github/workflows/issue_autodoc.yml
Comment thread .github/workflows/issue_autodoc.yml Outdated
Comment thread .github/workflows/issue_autodoc.yml Outdated
Comment thread .github/workflows/issue_autodoc.yml Outdated
Comment thread .github/workflows/issue_autodoc.yml Outdated
jdcmsd and others added 14 commits August 6, 2026 15:27
Replace uv/Python-script-based eval and finalize steps with:
- Python3 stdlib context assembly using gh CLI and grep
- Shell finalize using gh issue comment, curl for dotCMS API,
  and git commit/push
- prompts/burlap.txt read directly via cat (no YAML parsing)
- Fix jq null parent bug in preflight guard

Eliminates dependency on dotcms-aios PR merge order.
- Fix -f → -F on gh api PATCH so the file is read, not sent literally
- Prepend <!-- autodoc-report --> marker in finalize before posting so
  idempotent edit lookup works (marker was not in the report itself)
- Add --paginate to comment lookup to search beyond the first page
- Use jq first // empty instead of | head -1 to avoid SIGPIPE
- Fix urlTitle awk to use sub() consistent with title/tags/seoDescription
- Remove -k (TLS bypass); add --fail-with-body to dotCMS curl calls
- Add comment clarifying AUTODOC_DOTCMS_API_TOKEN_AISEARCH is used by
  the Claude subprocess via burlap.txt, not directly by a workflow step
- Make git push conditional on commit succeeding (|| true was causing
  unconditional push even when there was nothing new to commit)
- Add git push origin HEAD for explicit branch targeting
- Validate urlTitle is a URL slug before interpolating into JSON query
  (non-slug chars would break the shell-built -d payload)
- Validate IDENTIFIER matches UUID format before placing in URL
- Add comment on unpinned npm install explaining the tradeoff
Report lives as an issue comment — the repo copy is redundant.
Removing the push also drops the write-scope requirement on AUTODOC_AIOS_CI.
Embed burlap.txt content directly in the workflow as a heredoc step.
Update report path to /tmp — report is ephemeral, lives as issue comment.
dotcms-aios checkout now solely serves vault epic context lookup.
- Add conservatism clause to prompt: if Epic lacks technical detail,
  set action none rather than hallucinating a draft
- Restore <details> wrapping of doc draft in issue comment (was lost
  when post_report.py was replaced with inline shell)
- Replace label check for Epic with issue type check (github.event.issue.type.name)
- Update GraphQL parent lookup to use issueType { name } instead of labels
- Add id-token: write permission for Bedrock OIDC
- Replace npm install + claude --print with anthropics/claude-code-action@v1;
  model and Bedrock role sourced from vars.BEDROCK_MODEL_ID / vars.BEDROCK_ROLE_ARN
- Split finalize into three discrete steps for easier auditing:
  Post report comment / Apply doc update to dotCMS / Apply doc create to dotCMS
…tcms-aios checkout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck OIDC

Restructures the autodoc job into prep → claude → finalize so that the
Claude/Bedrock step runs inside dotCMS/ai-workflows/autodoc-executor.yml.
This sets job_workflow_ref to the ai-workflows ref, satisfying the trust
condition on GitHubActions-BedrockCodeReview that was blocking OIDC assumption.

The claude job references autodoc-executor.yml@v3.5.0 — pending the next
ai-workflows release after dotCMS/ai-workflows#<PR> merges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add modUser guardrail to the update path: fetch working inode before firing
  EDIT, skip with a clear log message if a human has an outstanding draft
  (live==false and modUser != CI Autodoc service account). Safe to proceed when
  live==true (no pending draft) or when the service account owns the working draft.
- Move AUTODOC_DOTCMS_SITE_FOLDER from secrets.* to vars.* — non-sensitive config
- Pass AUTODOC_DOTCMS_SITE_FOLDER as a with: input to the claude job

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… map

- Pin actions/checkout (both instances) to 8ade135 # v4.2.2
- Pin actions/download-artifact to fa0a91b # v4.1.8
- Replace secrets: inherit on the claude job with an explicit map of only
  the three secrets autodoc-executor.yml actually declares, satisfying
  least-privilege (Semgrep secrets-inherit finding)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Not a credential — move from secrets.* to vars.* in both the executor
with: block and the finalize job env, matching the AUTODOC_DOTCMS_SITE_FOLDER
pattern already in place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jdcmsd
jdcmsd force-pushed the autodoc-action-addition branch from b04fab0 to 28ed508 Compare August 6, 2026 19:27
@jdcmsd

jdcmsd commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Flurry of activity above is just a rebase to resolve a conflict. I think this is the same branch as merged the autodoc automation in originally; if we'd cut a new branch, it would have avoided the need for this step.

@jdcmsd
jdcmsd marked this pull request as ready for review August 7, 2026 14:39
@jdcmsd
jdcmsd requested a review from sfreudenthaler August 7, 2026 14:39
@github-actions github-actions Bot added the Area : CI/CD PR changes GitHub Actions/workflows label Aug 7, 2026
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Claude finished @sfreudenthaler's task in 3m 26s —— View job


Code Review

Focused on the latest push (job restructure prep → claude → finalize, artifact hand-off, and the new modUser guardrail). One real bug in the guardrail.

New Issues

  • 🟠 High: .github/workflows/issue_autodoc.yml:443the modUser guardrail's core branch is unreachable. IS_LIVE=$(echo "$CONTENT_CHECK" | jq -r '.entity.live // empty') uses jq's // operator, which returns the RHS when the LHS is null or false. So when .entity.live is boolean false — precisely the "a draft exists on top of the published version" case this guardrail exists to evaluate — IS_LIVE becomes the empty string. Execution then hits the if [ -z "$IS_LIVE" ] check at line 447 and skips with "content check returned unexpected response" — it never reaches the modUser comparison at line 452.

    Concrete failure: the automation saved a draft on a prior Epic-closure run (live=false, modUser=service account). On the next run it should recognize it owns the draft and re-apply the update, but instead it aborts as an "unexpected response." The intended live==false && modUser==service → safe path can never execute. (The overwrite-a-human-edit direction stays safe, so no data loss — but the feature's normal iterative path is broken.)

    Fix: don't use // on a boolean. e.g. IS_LIVE=$(echo "$CONTENT_CHECK" | jq -r '.entity.live') (yields true/false/null), then treat null as the unexpected-response case:

    IS_LIVE=$(echo "$CONTENT_CHECK" | jq -r '.entity.live')
    MOD_USER=$(echo "$CONTENT_CHECK" | jq -r '.entity.modUser // ""')
    if [ "$IS_LIVE" != "true" ] && [ "$IS_LIVE" != "false" ]; then
      echo "SKIP: content check returned unexpected response ..."
      exit 0
    fi

    Fix this →

Notes (non-blocking)

  • 🟡 Medium: .github/workflows/issue_autodoc.yml:91,328Assumption: the full eval context (Epic body + every related PR body + comments + vault + prompt) now crosses a job boundary as a job output and is then passed as the reusable-workflow prompt input. What to verify: that large Epics stay under GitHub's job-output / reusable-workflow input size limits — a silent truncation here would corrupt the prompt without an obvious error. Previously this string lived within a single job; the cross-job + reusable-workflow hop is new surface. Worth a quick check against your largest real Epic.

Resolved

  • .github/workflows/issue_autodoc.yml:324-326 — secrets map is complete and least-privilege: two explicit secrets, with the third moved to an input (per dotcms/ai-workflows#85). No secrets: inherit.
  • ✅ Action refs pinned to full commit SHAs (checkout, download-artifact) — resolves the earlier semgrep mutable-tag findings.
  • finalize correctly gated (always() && needs.claude.result != 'skipped') so it runs on claude failure but not when the chain is skipped via preflight.

· branch autodoc-action-addition

@jdcmsd jdcmsd linked an issue Aug 7, 2026 that may be closed by this pull request
jdcmsd and others added 2 commits August 7, 2026 12:32
…uardrail

Add permissions: id-token: write to the claude caller job so the reusable
workflow can mint an OIDC token — GitHub intersects caller and callee
permissions and id-token defaults to none without an explicit grant.

Harden the modUser content check: use --fail-with-body and treat curl
failures or unexpected responses as skip-with-warning rather than proceed,
closing the fail-open path that could clobber a human draft.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jdcmsd

jdcmsd commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

Last push to attempts to fix the issues raised above.

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

test-approval-check-ignore

@sfreudenthaler

Copy link
Copy Markdown
Member
  • Medium: .github/workflows/issue_autodoc.yml:324-326verify the secrets map is complete.

Should be good here. We dropped down to two secrets and moved the other one over to input in dotcms/ai-workflows#85

….yml

GitHub Actions runs steps under bash -eo pipefail. CONTENT_CHECK=$(curl ...)
is a bare assignment, so a failing curl aborts the script at that line under
set -e — the following `if [ $? -ne 0 ]` guard was dead code and the
intended graceful SKIP message never printed. Move the failure check onto
the assignment itself with `if ! CONTENT_CHECK=$(...)`, which is set -e-safe.

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

@sfreudenthaler sfreudenthaler left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Fixed the set -e dead-code issue in the content-check curl guard (.github/workflows/issue_autodoc.yml:437-443) — moved the failure check onto the assignment itself with if ! CONTENT_CHECK=$(...) so the SKIP message actually fires on a failed curl instead of the step just going red before the guard runs.

@jdcmsd
jdcmsd added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 429dc25 Aug 7, 2026
43 checks passed
@jdcmsd
jdcmsd deleted the autodoc-action-addition branch August 7, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : CI/CD PR changes GitHub Actions/workflows

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

ci(autodoc): fix Bedrock OIDC and harden autodoc workflow

2 participants