A justification is a snapshot of a decision: at the moment you wrote it, every claim held. But code, dependencies, and documents keep moving, and a fact you validated long ago can quietly stop being true. That is decision drift: the argument still looks fine on the page, yet one of its claims no longer holds, and the conclusion it once supported is now in jeopardy with nobody noticing.

The cure is to stop trusting the drawing and re-check the argument against reality, automatically. jPipe ships a GitHub Action that runs the Runner in your pipeline, so the whole justification is re-validated whenever you ask, and a drifted claim surfaces as a failed check instead of a silent lie.

This builds on the Runner tutorial: commit its compiled model (run/release.jd.json) and step library (run/release_lib.py) to the repository, here under a release-example/ project folder.

The workflow

Create .github/workflows/jpipe.yml. Any regular GitHub Actions trigger works here, a push, a pull_request, a schedule, so the justification can be re-validated on whatever event matters to you (we wire those up at the end). For this demonstration, though, we trigger it by hand with workflow_dispatch and expose one input, the version the changelog will announce, so you can watch the argument hold and then drift on command:

name: Justify the release

on:
  workflow_dispatch:
    inputs:
      changelog_version:
        description: "Version the changelog will announce (try 2.0, then 1.9)"
        default: "2.0"

permissions:
  contents: write        # push the generated diagram to a branch
  pull-requests: write   # comment on a pull request

jobs:
  justify:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Prepare the mock evidence
        working-directory: release-example
        env:
          CHANGELOG_VERSION: ${{ inputs.changelog_version }}
        run: |
          mkdir -p mock
          touch mock/tests.ok
          echo "## $CHANGELOG_VERSION" > mock/CHANGELOG.md

      - name: Run the jPipe justification
        uses: jpipe-mcscert/jpipe-runner@v3.5.3
        with:
          working_directory: release-example
          jd_file: "run/release.jd.json"
          library: |
            run/release_lib.py
          embed_image: true
          github-token: ${{ secrets.GITHUB_TOKEN }}

The middle step is the demonstration’s trick: it fabricates the same mock/ evidence you used locally, but writes the changelog from the changelog_version input passed in as an environment variable. In a real project you would delete this step, since the changelog and test results already live in the repository, and let the action check them as they are. The final step installs the runner, executes your justification against the library, and (embed_image: true) commits the coloured diagram, posting it back as a comment when the run belongs to a pull request. Both steps run inside release-example/ (the working-directory / working_directory keys), where the project’s run/ and mock/ folders live, so every path in the workflow stays relative to it.

Run it: the argument holds

Push the workflow, open the repository’s Actions tab, choose Justify the release, and click Run workflow. Leave changelog_version at its default 2.0 and launch. The changelog the step writes names 2.0, every gate passes, and the run is green:

📸 Screenshot needed: the manual run with changelog_version = 2.0, passing (green).

…until a decision drifts

Run it again, but set changelog_version to 1.9, as if the release were cut while the changelog was never updated past the previous version. The step now writes a changelog that never mentions 2.0, so “the changelog is up to date” fails, the strategy and conclusion above it are skipped, and the run goes red:

📸 Screenshot needed: the manual run with changelog_version = 1.9, failing (red).

That red check is decision drift caught in the act: nothing about the argument changed, only the world it describes, and the pipeline refused to let the stale claim slip through unnoticed.

From demo to guardrail

Delete the mock-writing step and trigger on real changes to turn this into a standing guardrail:

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

Now every pull request re-validates the whole justification against the actual repository. With embed_image: true and github-token, the action commits the coloured diagram and posts it as a PR comment, so reviewers see the argument’s live status inline:

📸 Screenshot needed: the PR comment with the embedded justification diagram.

A failing claim turns the check red and blocks the merge: the very drift you triggered by hand, now caught automatically on every change.

Where to next?

Full action reference: jpipe-runner/docs/ACTION.md.

Updated: