diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1865cb8..7197a1f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -120,6 +120,37 @@ jobs: chock plugin build --repo catalog --policies-dir base --format codex --out-dir dist chock marketplace build --dist dist --tree codex + # What the published tree was built from, as commits rather than names: a tag or branch + # moves, a SHA does not. The file is written at publish time, not by the build, so + # `chock marketplace build` and the generated-only check neither produce nor compare it. + - name: Record the commits this tree was built from + env: + # Dispatch inputs are typed by whoever runs the workflow; expanded by the runner + # into shell text they would execute, through the environment they stay data. + CATALOG_REF: ${{ inputs.catalog_ref }} + FRAMEWORK_REF: ${{ steps.framework.outputs.ref }} + run: | + catalog_sha="$(git -C catalog rev-parse HEAD)" + framework_sha="$(git -C framework rev-parse HEAD)" + echo "catalog_sha=$catalog_sha" >> "$GITHUB_ENV" + echo "framework_sha=$framework_sha" >> "$GITHUB_ENV" + # JSON quoting keeps a ref with a colon or quote valid YAML. + python3 - dist/generated_from.yaml "$CATALOG_REF" "$catalog_sha" "$FRAMEWORK_REF" "$framework_sha" <<'PY' + import json, sys + out, catalog_ref, catalog_sha, framework_ref, framework_sha = sys.argv[1:] + q = json.dumps + with open(out, "w") as f: + f.write("catalog:\n") + f.write(" repository: open-coder-ai/chock-catalog\n") + f.write(f" ref: {q(catalog_ref)}\n") + f.write(f" sha: {q(catalog_sha)}\n") + f.write("framework:\n") + f.write(" repository: open-coder-ai/chock\n") + f.write(f" ref: {q(framework_ref)}\n") + f.write(f" sha: {q(framework_sha)}\n") + PY + cat dist/generated_from.yaml + - name: Show what would change working-directory: dist run: | @@ -151,5 +182,5 @@ jobs: exit 0 fi git add -A - git commit -m "generated: catalog $CATALOG_REF (framework $FRAMEWORK_REF)" + git commit -m "generated: catalog $CATALOG_REF @ $catalog_sha (framework $FRAMEWORK_REF @ $framework_sha)" git push diff --git a/README.md b/README.md index 6840dca..86c0180 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ guard that runs and crashes is a **deny** here, because Codex rejects the confir other clients show, and a gate that cannot reach a decision refuses rather than allowing something it never judged. +**How a deny reaches Codex.** Each guard plugin wires one `PreToolUse` hook, and the hook +answers on stdout with a JSON decision, not an exit code: +`{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "..."}}` +with exit status 0. Gate plugins wire the same `PreToolUse` hook plus a `Stop` hook. The one +exit-code answer is exit 2, used only when no working Python 3.11+ is found. + ## Install Codex reads this repository as a git marketplace, the same mechanism as its Plugins UI: