[{"data":1,"prerenderedAt":15},["ShallowReactive",2],{"doc-gate-ci":3},{"blocks":4,"markdown":14},[5,8,12],{"kind":6,"html":7},"html","\u003Cp>If an artifact your build ships is made from an intent, make it in CI through\nForma and let the verdict decide: \u003Ccode class=\"chip chip--valid\">valid\u003C\u002Fcode> writes the artifact and its citation,\nanything else fails the build. Forma does not check an artifact you already have\n— it checks the one it returns.\u003C\u002Fp>\n\u003Ch2 id=\"the-script\">The script\u003C\u002Fh2>\n",{"kind":9,"lang":10,"code":11},"code","bash","#!\u002Fusr\u002Fbin\u002Fenv bash\nset -euo pipefail\n\nanswer=$(curl -sS --fail-with-body \\\n  \"https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask\" \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d @intent.json)\n\nstatus=$(jq -r .status \u003C\u003C\u003C\"$answer\")\n\ncase \"$status\" in\n  valid)\n    jq -r .result \u003C\u003C\u003C\"$answer\" > artifact.json\n    echo \"checked against $(jq -r .verified_by \u003C\u003C\u003C\"$answer\")\"\n    ;;\n  invalid)\n    echo \"::error::the check refused it: $(jq -r .detail \u003C\u003C\u003C\"$answer\")\"\n    exit 1\n    ;;\n  error)\n    echo \"::warning::the oracle could not answer; nothing was billed\"\n    exit 75          # EX_TEMPFAIL — retry the job, do not fail the branch\n    ;;\n  *)\n    echo \"::error::unexpected answer: $status\"\n    exit 1\n    ;;\nesac",{"kind":6,"html":13},"\u003Cp>\u003Ccode>--fail-with-body\u003C\u002Fcode> matters: without it \u003Ccode>curl\u003C\u002Fcode> exits 0 on a \u003Ccode>401\u003C\u002Fcode> and the script\nparses an error envelope as a verdict.\u003C\u002Fp>\n\u003Ch2 id=\"distinguish-refused-from-unavailable\">Distinguish &quot;refused&quot; from &quot;unavailable&quot;\u003C\u002Fh2>\n\u003Cp>\u003Ccode class=\"chip chip--invalid\">invalid\u003C\u002Fcode> means the artifact is wrong — fail the build; a human should look.\u003C\u002Fp>\n\u003Cp>\u003Ccode class=\"chip chip--error\">error\u003C\u002Fcode> means we were unavailable — that is not the branch's fault. Exiting 75\nrather than 1 lets a retry policy pick it up without a red mark against someone's\ncommit.\u003C\u002Fp>\n\u003Ch2 id=\"keep-the-citation\">Keep the citation\u003C\u002Fh2>\n\u003Cp>Write \u003Ccode>verified_by\u003C\u002Fcode> into the build output next to the artifact. It is what tells\na reader six months later which version of the rules the file in the repository passed.\u003C\u002Fp>\n\u003Ch2 id=\"budget\">Budget\u003C\u002Fh2>\n\u003Cp>Every gated build spends \u003Ca href=\"\u002Flimits\">credits\u003C\u002Fa>. On a busy repository, gate on\nmerge rather than on every push. \u003Ca href=\"https:\u002F\u002Fapp.formalogic.io\u002Fusage\" target=\"_blank\" rel=\"noopener noreferrer\">Usage\u003C\u002Fa> shows\nwhat this period has spent and \u003Ca href=\"https:\u002F\u002Fapp.formalogic.io\u002Fbilling\" target=\"_blank\" rel=\"noopener noreferrer\">Credits\u003C\u002Fa> your\nbalance — check both before turning it on across many pipelines.\u003C\u002Fp>\n","# Gate CI on a verdict\n\n> Generate an artifact in CI and fail the build unless it comes back valid.\n\nIf an artifact your build ships is made from an intent, make it in CI through\nForma and let the verdict decide: `valid` writes the artifact and its citation,\nanything else fails the build. Forma does not check an artifact you already have\n— it checks the one it returns.\n\n## The script\n\n```bash\n#!\u002Fusr\u002Fbin\u002Fenv bash\nset -euo pipefail\n\nanswer=$(curl -sS --fail-with-body \\\n  \"https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask\" \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d @intent.json)\n\nstatus=$(jq -r .status \u003C\u003C\u003C\"$answer\")\n\ncase \"$status\" in\n  valid)\n    jq -r .result \u003C\u003C\u003C\"$answer\" > artifact.json\n    echo \"checked against $(jq -r .verified_by \u003C\u003C\u003C\"$answer\")\"\n    ;;\n  invalid)\n    echo \"::error::the check refused it: $(jq -r .detail \u003C\u003C\u003C\"$answer\")\"\n    exit 1\n    ;;\n  error)\n    echo \"::warning::the oracle could not answer; nothing was billed\"\n    exit 75          # EX_TEMPFAIL — retry the job, do not fail the branch\n    ;;\n  *)\n    echo \"::error::unexpected answer: $status\"\n    exit 1\n    ;;\nesac\n```\n\n`--fail-with-body` matters: without it `curl` exits 0 on a `401` and the script\nparses an error envelope as a verdict.\n\n## Distinguish \"refused\" from \"unavailable\"\n\n`invalid` means the artifact is wrong — fail the build; a human should look.\n\n`error` means we were unavailable — that is not the branch's fault. Exiting 75\nrather than 1 lets a retry policy pick it up without a red mark against someone's\ncommit.\n\n## Keep the citation\n\nWrite `verified_by` into the build output next to the artifact. It is what tells\na reader six months later which version of the rules the file in the repository passed.\n\n## Budget\n\nEvery gated build spends [credits](https:\u002F\u002Fdocs.formalogic.io\u002Flimits). On a busy repository, gate on\nmerge rather than on every push. [Usage](https:\u002F\u002Fapp.formalogic.io\u002Fusage) shows\nwhat this period has spent and [Credits](https:\u002F\u002Fapp.formalogic.io\u002Fbilling) your\nbalance — check both before turning it on across many pipelines.\n",1793819974137]