Skip to content

feat(providers): contract 1.2.0 — target_dir and run options for hooks (#405 prep) - #440

Merged
apotema merged 2 commits into
developmentfrom
feat/contract-1.2.0
Sep 26, 2026
Merged

apotema merged 2 commits into
developmentfrom
feat/contract-1.2.0

Conversation

@apotema

@apotema apotema commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

PR 0b of the #405 plan (android as the first provider). Stacked on #439 (main → development sync).

Contract 1.1.0 gave hooks no target directory and no run options, so a replace run hook couldn't receive --scene/--screenshot/--after, and a bundle hook with --output lost track of the generated tree. This PR adds a generic, additive contract 1.2.0. The CLI does no platform mapping, and the agnosticism guard stays green.

Wire

  • Version 1.2.0; the CLI speaks {1.2.0, 1.1.0, 1.0.0}. New carriesRunContext(wire) next to carriesBuildNumber.
  • target_dir is required on every 1.2.0 context. On a hook it's the absolute .labelle/<backend>_<target>/, the same for every step whatever output_dir is (including bundle --output). It's null on commands and never emitted below 1.2.0.
  • run is present on every run-step hook of a 1.2.0 wire and absent everywhere else. Shape: { "env": [{"name","value"}…], "args": [tokens after --], "timeout_ms": int|null }.
    • env is exactly what runner.appendRunOptionEnv(runOptionEnv(parsed_args)) produces for the core launch.
    • Other run-step hooks, such as wasm serve's, get the empty set.
  • A provider capped below 1.2.0 gets the older wire with neither key. If run options were passed to its run hook, the CLI prints one labelle: note: run options not passed to '<pkg>/<id>' (provider contract <wire> < 1.2.0) line.
  • The strict decoder checks which keys are present on each wire: target_dir is required at 1.2.0 and unknown below it, optional keys are never null, run is only allowed on run-step hooks, and env names must be well formed.

Plumbing: provider_hooks.Site gains target_dir (required) and run_options. pipeline.zig fills them in; run_options is set only for labelle run, through pipeline/run.zig hookRunOptions.

Tests

  • Unit tests:
    • the decoder accept/reject matrix
    • negotiation for the 1.2, 1.1 and 1.0 ranges
    • runPhase hands out target_dir for every step, and run only for the run step
    • hookRunOptions matches the core launch list, with the headless knobs excluded
  • e2e in test/provider_hooks_e2e.py, on a provider target with replace hooks on all four steps:
    • labelle run --scene=x --screenshot=s --after=2s --timeout=30s -- a b: the replace-run hook's context has the three LABELLE_* pairs, args == ["a","b"] and timeout_ms == 30000.
    • target_dir is correct for build, run, bundle and bundle --output <elsewhere>.
    • A host-launch sentinel planted at the launch path never runs. As a control, the same command without the replacement does run it.
    • A provider capped at <1.2.0 gets neither key and exactly one note; with no options there is no note.
  • Existing e2e assertions updated for the new newest wire (dispatch, targets). The fixture probe now logs its full context.

Docs: docs/provider-contract-v1.md (additive minor, schema, negotiation) and docs/provider-hooks.md.

Verified locally (macOS, Zig 0.16.0), each exit code checked separately:

  • zig fmt --check on the touched files
  • zig build
  • zig build -Dtarget=x86_64-windows
  • zig build test: 1082/1087 passed (5 skipped), guard included
  • zig build test-provider-dispatch
  • Python e2e, all exit 0: plugin_compat, provider_config (21), provider_dispatch (25), provider_github (70), provider_hooks (50, up from 43), provider_targets (40)

Note: CI's pull_request trigger lists main, development and feat/**. A PR whose base is chore/sync-main-into-development won't get CI until #439 merges and this PR is retargeted to development.

https://claude.ai/code/session_01R2K5sJDL5Msn1uRTkybiDP


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

#405 prep)

Additive minor of the provider contract (wire list 1.2.0, 1.1.0, 1.0.0):

- `target_dir`: required on every 1.2.0 context — the absolute generated
  `.labelle/<backend>_<target>/` on hooks (same for every step, whatever
  `output_dir` is, `bundle --output` included), null on commands; never
  emitted below 1.2.0.
- `run`: on every `run`-step hook of a 1.2.0 wire, absent elsewhere:
  `{ env: [{name,value}], args, timeout_ms }`, where `env` is exactly the
  `runner.appendRunOptionEnv(runOptionEnv(..))` list the core launch sets,
  `args` the tokens after `--`, `timeout_ms` the `--timeout`. The CLI maps
  nothing to any platform.
- `provider_hooks.Site` gains `target_dir` and `run_options`, filled by the
  pipeline (`run_options` only for `labelle run`; other run-step hooks get
  the empty set).
- A provider capped below 1.2.0 gets the older wire with neither key and,
  when run options were passed to its run hook, one `note:` line.
- Decoder: strict key presence per wire (`target_dir` required at 1.2.0,
  unknown below it; optional keys never null), run only on run-step hooks,
  well-formed env names.

Tests: decoder accept/reject matrix, negotiation (1.2/1.1/1.0 ranges),
runPhase plumbing, hookRunOptions vs the core launch list, and an e2e
replace-run hook on a provider target recording its context while a
planted host-launch sentinel (which the unreplaced run does execute) never
runs. Docs: provider-contract-v1.md, provider-hooks.md.

Claude-Session: https://claude.ai/code/session_01R2K5sJDL5Msn1uRTkybiDP
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: a37e3645-6cd0-4f27-9aea-34ea832bee34

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-26T23:14:20.107337Z cc2fce9 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@apotema
apotema changed the base branch from chore/sync-main-into-development to development September 26, 2026 23:03
@apotema

apotema commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: cc2fce95cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant