From 30f3d8e39ae355cdc120c214b1b79ee76208570a Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Fri, 2 Oct 2026 10:40:33 +0200 Subject: [PATCH 1/2] Pass context-sync through to kapi-action From kapi 1.3 a project's terms, voice profiles, content memory and recorded decisions live in kapi's workspace outside git, and a runner starts with an empty one. Both workflows take a context-sync input and pass it to kapi-action@v1, which runs kapi context pull before the command and, with true, kapi context push after it. The default, false, keeps the current behaviour. The gate workflow's comment says an established threshold needs the pull, in place of the retired reviewed threshold and kapi commit. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PWc9qYQJ93y88XrN6TkMvT --- .github/workflows/gate.yml | 12 +++++++++--- .github/workflows/up.yml | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index 0bbb9a0..f2610fa 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -12,9 +12,10 @@ name: kapi ship gate # pull-requests: write # # Ordinary builds never fail on target-language drift; the gate is the -# explicit, opt-in enforcement point. A `reviewed` threshold reads the -# decisions `kapi commit` writes under .kapi/state/, so commit them in the PR -# or the runner sees no approvals. +# explicit, opt-in enforcement point. An `established` threshold counts the +# approvals recorded in the project's context, which lives in kapi's workspace +# outside git. A project that shares its context through a backend needs +# `context-sync: pull` (kapi 1.3 and later), or the runner sees no approvals. on: workflow_call: @@ -43,6 +44,10 @@ on: description: "Commits to fetch, 0 for all. A diff-scoped check (--diff-range, --diff-against) reads the commits it compares, so set 0 for one" type: number default: 1 + context-sync: + description: "kapi-action's context-sync: pull runs kapi context pull before the check, so the gate reads the project's context from the backend its kapi.yaml declares (kapi 1.3 and later); false skips it" + type: string + default: "false" server: description: "Bowrain server URL (exported as BOWRAIN_SERVER_URL)" type: string @@ -84,3 +89,4 @@ jobs: args: ${{ inputs.args }} project: ${{ inputs.project }} pr-comment: ${{ inputs.pr-comment }} + context-sync: ${{ inputs.context-sync }} diff --git a/.github/workflows/up.yml b/.github/workflows/up.yml index 337ff75..0f7ddf5 100644 --- a/.github/workflows/up.yml +++ b/.github/workflows/up.yml @@ -32,6 +32,10 @@ on: description: "Fail the workflow when the run parks instead of delivering partial progress" type: boolean default: false + context-sync: + description: "kapi-action's context-sync: pull runs kapi context pull before kapi up, and true also runs kapi context push after it, through the backend the project's kapi.yaml declares (kapi 1.3 and later); false skips both" + type: string + default: "false" plugins: description: "Plugin refs for setup-kapi (registry names)" type: string @@ -106,6 +110,7 @@ jobs: project: ${{ inputs.project }} args: ${{ inputs.args }} fail-on-parked: ${{ inputs.fail-on-parked }} + context-sync: ${{ inputs.context-sync }} - name: Deliver as a pull request id: deliver-pr From d5845c1f7c9f4d9305795856b11e29d359f09ab6 Mon Sep 17 00:00:00 2001 From: Asgeir Frimannsson Date: Fri, 2 Oct 2026 10:41:25 +0200 Subject: [PATCH 2/2] Describe the workflows in kapi 1.3 terms The README describes kapi as a content engine rather than a continuous-localization path. The run description names the content memory, terms and voice, settles the source first, and says a unit with a failing finding stays translated and holds its language out of shipping. Approvals are recorded in the project's context and shared through its context backend or the connected server, in place of kapi commit and .kapi/state/, and raise established coverage. The gate text names the voice, terms and QA gates, says each finding fails or reports, and covers a check that did not run. Both input lists gain context-sync. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01PWc9qYQJ93y88XrN6TkMvT --- .github/workflows/gate.yml | 2 +- README.md | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index f2610fa..c8af951 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -62,7 +62,7 @@ on: required: false outputs: gate: - description: "pass or fail" + description: "pass or fail; empty when the check did not run" value: ${{ jobs.gate.outputs.gate }} jobs: diff --git a/README.md b/README.md index d99411b..8ff5489 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # kapi-workflows -Reusable GitHub Actions workflows for [kapi](https://github.com/neokapi/neokapi) — the whole continuous-localization happy path as one `uses:` line each. They compose [`setup-kapi`](https://github.com/neokapi/setup-kapi) and [`kapi-action`](https://github.com/neokapi/kapi-action); use those directly when you need a custom job shape. +Reusable GitHub Actions workflows for [kapi](https://github.com/neokapi/neokapi), the format-aware content engine. With one `uses:` line, `up.yml` brings a project's content to the state its `kapi.yaml` recipe declares, translations included, and delivers the result as a pull request, and `gate.yml` fails pull requests that do not meet the project's content quality gates. They compose [`setup-kapi`](https://github.com/neokapi/setup-kapi) and [`kapi-action`](https://github.com/neokapi/kapi-action); use those directly when you need a custom job shape. ## `up.yml` — catch up on a schedule, deliver a PR @@ -24,26 +24,26 @@ jobs: Runs `kapi up` — the kapi loop — and opens a pull request with the produced translations and a kapi up report (outcome, passes, parked locales). A run that **parks** (work remains that needs a person) still delivers what it caught up; set `fail-on-parked: true` to block instead. -Inputs: `project`, `args`, `create-pull-request` (default `true`), `fail-on-parked`, `plugins` (default `bowrain`), `kapi-version`, `server`, `runs-on`. Secrets: `bowrain-auth-token`, `anthropic-api-key`, `deliver-token` (a PAT or App token when CI/deploys should react to the delivered translations — the default `GITHUB_TOKEN` triggers no workflows). Outputs: `outcome`, `passes`, `parked-locales`, `has-changes`, `pull-request-url`. +Inputs: `project`, `args`, `create-pull-request` (default `true`), `fail-on-parked`, `context-sync` (default `false`), `plugins` (default `bowrain`), `kapi-version`, `server`, `runs-on`. Secrets: `bowrain-auth-token`, `anthropic-api-key`, `deliver-token` (a PAT or App token when CI/deploys should react to the delivered translations; the default `GITHUB_TOKEN` triggers no workflows). Outputs: `outcome`, `passes`, `parked-locales`, `has-changes`, `pull-request-url`. ### What a run does -Each pass, for every language behind its ship gate: **reuse** exact translation-memory matches first (free), **translate** what remains with the configured AI provider plus the project's terminology, then **check** what was produced — placeholder integrity, inline tags, do-not-translate terms. A unit with a failing finding counts as *drafted*, not translated, so bad output can never lift a language over its gate. Passes repeat until every gate clears or nothing progresses. +`kapi up` settles the source first: the source checks run, and a block whose source is below the recipe's `defaults.translate_after` level is held rather than translated. Then, each pass, for every language behind its ship gate: **reuse** exact content-memory matches first (free), **translate** what remains with the configured AI provider plus the project's terms and voice, then **check** what was produced: placeholder integrity, inline tags, do-not-translate terms. A unit with a failing finding still counts as *translated*, and the finding holds its language out of shipping until it is fixed, so bad output never lifts a language over its gate. Passes repeat until every gate clears or nothing progresses. ```mermaid flowchart LR S[source changes] --> U[kapi up] subgraph PASS ["each pass, per language behind its gate"] - TM["1 · reuse
TM exact matches"] --> AI["2 · translate
AI + terminology"] --> CK["3 · check
placeholders · terms · tags"] + MEM["1 · reuse
content memory exact matches"] --> AI["2 · translate
AI + terminology"] --> CK["3 · check
placeholders · terms · tags"] end U --> PASS CK -->|every gate met| CV["up to date
PR with translations"] CK -->|needs a person| PK["parked
the review queue"] - PK --> RV["review & approve
committed under .kapi/state"] + PK --> RV["review & approve
recorded in the project's context"] RV -.->|next run sees it| U ``` -Parked work is the review queue, not an error: a person reviews and approves it, `kapi commit` records the decision under `.kapi/state/` (or the connected server records it), and the `reviewed` coverage the ship gate measures goes up. The next run and the next gate see it. +Parked work is the review queue, not an error: a person reviews and approves it, and the approval is recorded in the project's context (with `kapi apply` or the Review page of Kapi Desktop), or the connected server records it. A machine takes a target unit from *draft* to *translated*; an approval takes it to *established* and raises the `established` coverage the ship gate measures. From kapi 1.3 the project's context (terms, voice profiles, content memory and recorded decisions) lives in kapi's workspace outside git, and a project shares it through the context backend its `kapi.yaml` declares, with `kapi context push` and `kapi context pull`. Set `context-sync: true` on `up.yml` so a run takes in what the team pushed and pushes what it recorded; the next run and the next gate then see the approval. ## `gate.yml` — fail PRs on unmet content quality gates @@ -61,9 +61,9 @@ jobs: pull-requests: write ``` -Runs `kapi check --ship`: the project's bound gates (voice, terminology, rule-based checks) plus its ship and source coverage gates. An unmet gate exits `3`, fails the job with a distinct "gate unmet" annotation, and posts one sticky report comment on the PR. Ordinary builds never fail on target-language drift; the gate is the explicit, opt-in enforcement point. +Runs `kapi check --ship`: the project's bound gates (voice, terms and QA) plus its ship and source coverage gates. Each finding either fails its gate or only reports. An unmet gate exits `3`, fails the job with a distinct "gate unmet" annotation, and posts one sticky report comment on the PR. A check that did not run, because it examined no content or a checker could not show it is able to fail, exits `4` and fails the job with the cause. Ordinary builds never fail on target-language drift; the gate is the explicit, opt-in enforcement point. An `established` threshold counts the approvals in the project's context, so a project that shares its context through a backend sets `context-sync: pull`. -Inputs: `project`, `args` (default `--ship`), `plugins`, `kapi-version`, `pr-comment` (default `true`), `fetch-depth` (default `1`), `server`, `runs-on`. Output: `gate` (`pass`/`fail`). +Inputs: `project`, `args` (default `--ship`), `plugins`, `kapi-version`, `pr-comment` (default `true`), `fetch-depth` (default `1`), `context-sync` (default `false`), `server`, `runs-on`. Output: `gate` (`pass`/`fail`, empty when the check did not run). A diff-scoped check reads the commits it compares, so pass `fetch-depth: 0` with `--diff-range` or `--diff-against`: