From 6ab6635237aba8172812406499186a7fbc8a16cd Mon Sep 17 00:00:00 2001 From: bobai Date: Thu, 3 Sep 2026 10:08:48 +0100 Subject: [PATCH 1/4] test: stabilize runtime proof dependency fixture --- .../scripts/observe_marker.py | 19 +++++++++++++++---- .../scripts/produce_marker.py | 18 ++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/reference/runtime-proof-evidence/scripts/observe_marker.py b/reference/runtime-proof-evidence/scripts/observe_marker.py index 0127ffe..17e6a18 100644 --- a/reference/runtime-proof-evidence/scripts/observe_marker.py +++ b/reference/runtime-proof-evidence/scripts/observe_marker.py @@ -3,14 +3,26 @@ import json import os +import time +import urllib.error import urllib.request from pathlib import Path -with urllib.request.urlopen("http://127.0.0.1:8081/marker", timeout=3) as response: - marker = response.read().decode() +marker = None +for _ in range(30): + try: + with urllib.request.urlopen("http://127.0.0.1:8081/marker", timeout=3) as response: + if response.status == 200: + candidate = response.read().decode() + if candidate: + marker = candidate + break + except (OSError, urllib.error.URLError): + pass + time.sleep(0.2) if not marker: - raise RuntimeError("dependency did not return the proof marker") + raise RuntimeError("dependency did not return the proof marker within the bounded retry window") Path(os.environ["OTA_PROOF_ATTESTATION_FILE"]).write_text( json.dumps( @@ -21,4 +33,3 @@ } ) ) - diff --git a/reference/runtime-proof-evidence/scripts/produce_marker.py b/reference/runtime-proof-evidence/scripts/produce_marker.py index a24a795..e0e7d57 100644 --- a/reference/runtime-proof-evidence/scripts/produce_marker.py +++ b/reference/runtime-proof-evidence/scripts/produce_marker.py @@ -2,11 +2,21 @@ # Licensed under the Apache License, Version 2.0. See LICENSE for the full license text. import os +import time +import urllib.error import urllib.request marker = os.environ["OTA_PROOF_DEPENDENCY_MARKER"].encode() request = urllib.request.Request("http://127.0.0.1:8081/marker", data=marker, method="POST") -with urllib.request.urlopen(request, timeout=3) as response: - if response.status != 204: - raise RuntimeError(f"unexpected dependency response: {response.status}") - +last_error = None +for _ in range(30): + try: + with urllib.request.urlopen(request, timeout=3) as response: + if response.status != 204: + raise RuntimeError(f"unexpected dependency response: {response.status}") + break + except (OSError, urllib.error.URLError) as error: + last_error = error + time.sleep(0.2) +else: + raise RuntimeError(f"dependency did not accept the proof marker: {last_error}") From a565b22ae86e1840c9ecfe7fb97dd0c0bff9cc4b Mon Sep 17 00:00:00 2001 From: bobai Date: Tue, 8 Sep 2026 21:43:00 +0100 Subject: [PATCH 2/4] examples: model selected native and container setup --- README.md | 7 +- execution/README.md | 3 +- execution/container/node-service/README.md | 18 ++++- .../node-service/native-ready.example | 1 + execution/container/node-service/ota.yaml | 71 ++++++++++++++++--- .../container/node-service/pnpm-lock.yaml | 9 +++ 6 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 execution/container/node-service/native-ready.example create mode 100644 execution/container/node-service/pnpm-lock.yaml diff --git a/README.md b/README.md index 1a760f1..2d02106 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,9 @@ Use these as starting points when you want: - `reference/` - canonical, production-adjacent repo examples Every example contract declares `metadata.ota.minimum_version`. That floor is the oldest Ota -version the example is intended to run with; examples using the current v1.6.26 authority and -execution surfaces require Ota v1.6.26 or later. Ota refuses a contract whose minimum exceeds the -installed binary before validation, setup, or task execution. +version the example is intended to run with, so use each contract's declared floor rather than +assuming one repository-wide version. Ota refuses a contract whose minimum exceeds the installed +binary before validation, setup, or task execution. ## Choose by problem @@ -158,6 +158,7 @@ Use the public reference for the governing model, then copy the nearest example: | Adopt Ota in an existing repo | [`reference/adoption-flow`](reference/adoption-flow) | Flagship Java/Maven adoption with services, task prerequisites, docs, and release companions. | | Model a production-shaped team repo | [`reference/canonical-team-repo`](reference/canonical-team-repo) | Env policy, reusable surfaces, probe-backed readiness, and release-only requirements. | | Add CI, container, or remote execution | [`ci`](ci) or [`execution`](execution) | Provider patterns and execution-boundary examples. | +| Keep host-only setup out of container CI | [`execution/container/node-service`](execution/container/node-service) | Uses `execution.modes.native.depends_on` so aggregate verification follows only the selected backend graph. | | Bootstrap several repositories together | [`workspace/adoption-flow`](workspace/adoption-flow) | First-week workspace adoption and multi-repo readiness. | ### Setup, toolchains, and hydration diff --git a/execution/README.md b/execution/README.md index aa5459e..b8453c7 100644 --- a/execution/README.md +++ b/execution/README.md @@ -38,6 +38,7 @@ Use this theme when ota should run repo tasks through a backend boundary instead - how to keep container execution explicit - how to keep remote execution explicit - how to preserve one repo contract across multiple execution locations +- how mode-specific dependencies keep host-only preparation out of container execution ## Folders @@ -55,7 +56,7 @@ Use this theme when ota should run repo tasks through a backend boundary instead 5. `local-topology/shared-local-backend-fulfillment/README.md` 6. `local-topology/shared-local-backend-environment/README.md` 7. `container/template/README.md` -8. `container/node-service/README.md` +8. `container/node-service/README.md` for mode-specific dependency and selected-graph behavior 9. `remote/README.md` 10. `remote/template/README.md` 11. `remote/ssh-node-service/README.md` diff --git a/execution/container/node-service/README.md b/execution/container/node-service/README.md index e40d5b6..d4175f8 100644 --- a/execution/container/node-service/README.md +++ b/execution/container/node-service/README.md @@ -31,8 +31,10 @@ A copyable starting point for a Node service that runs in a container context, k - keeps local and CI runtime behavior aligned - makes the execution boundary obvious - gives `ota doctor` and `ota run` a stable path -- keeps `run: pnpm dev` simple while ota owns host URL resolution +- keeps the structured `pnpm dev` launch explicit while ota owns host URL resolution - keeps one canonical task intent (`start`) while still supporting `--mode native` and mode-specific lifecycle +- keeps native-only local preparation out of the container verification graph through + `execution.modes.native.depends_on` - helps a team standardize Node setup without turning the README into a shell-script graveyard ## Use when @@ -54,6 +56,8 @@ ota run setup ota run start # open the URL ota prints (same value as OTA_PUBLIC_URL) ota run start --mode native +ota run verify --agent +ota run verify --mode native --agent ``` ## Projection contract @@ -70,6 +74,14 @@ ota run start --mode native Ota binds the repo source into the container as usual, but overlays `node_modules` with an engine-managed named volume under the execution context so platform-specific binaries stay compatible with the container image. +`tasks.setup`, `tasks.test`, and the aggregate `tasks.verify` show the selected dependency +boundary. Container verification selects `setup -> test -> verify`; native verification selects +`setup:host:local -> setup -> test -> verify`. The native-only local preparation does not make +the container aggregate unavailable and does not enter its selected graph identity. In a real +repository that prerequisite may materialize `.env.local`, a host tool shim, or another +host-specific input; the dependency belongs only on the mode that consumes it, and container +execution does not read or report an optional env source owned only by that native branch. + ## Included app This folder includes a minimal runnable app: @@ -77,7 +89,9 @@ This folder includes a minimal runnable app: - `package.json` with `dev` and `test` scripts - `src/server.mjs` that serves `http` and `metrics`, and prints `OTA_PUBLIC_URL` plus `OTA_PUBLIC_URL_METRICS` -Run `ota run test` if you want to verify the example task chain without starting the long-running dev server. +Run `ota run verify --agent` if you want to exercise the complete container task chain without +starting the long-running dev server. Use `ota run verify --mode native --agent` to exercise the +host-specific preparation path. Run `ota run clean:start` if you want the same app intent with container `lifecycle: ephemeral`. ## Reset Ota-managed state diff --git a/execution/container/node-service/native-ready.example b/execution/container/node-service/native-ready.example new file mode 100644 index 0000000..1287896 --- /dev/null +++ b/execution/container/node-service/native-ready.example @@ -0,0 +1 @@ +prepared-by=ota diff --git a/execution/container/node-service/ota.yaml b/execution/container/node-service/ota.yaml index a16040c..e78ea53 100644 --- a/execution/container/node-service/ota.yaml +++ b/execution/container/node-service/ota.yaml @@ -24,16 +24,19 @@ version: 1 metadata: ota: - minimum_version: "1.6.23" + minimum_version: "1.6.28" project: name: node-service-container description: Node service template with Ota container mode for setup, dev, and test toolchains: node: - version: "22" + version: ">=22" package_managers: pnpm: "10.0.0" + fulfillment: + source: corepack + mode: run execution: default_context: app contexts: @@ -54,8 +57,27 @@ execution: isolated_paths: - node_modules tasks: + setup:host:local: + description: Create a host-only local preparation marker when native execution is selected. + category: setup + execution: + default_mode: native + action: + kind: copy_if_missing + from: native-ready.example + to: .local/native-ready + safe_for_agent: true + internal: true setup: - context: app + execution: + default_mode: container + modes: + native: + context: host + depends_on: + - setup:host:local + container: + context: app prepare: kind: dependency_hydration medium: package_dependencies @@ -85,7 +107,10 @@ tasks: HOSTNAME: 127.0.0.1 PORT: "3000" METRICS_PORT: "9090" - run: pnpm dev + launch: + kind: command + exe: pnpm + args: [dev] container: context: app lifecycle: persistent @@ -93,7 +118,10 @@ tasks: HOSTNAME: 0.0.0.0 PORT: "3000" METRICS_PORT: "9090" - run: pnpm dev + launch: + kind: command + exe: pnpm + args: [dev] runtime: kind: service listeners: @@ -134,7 +162,10 @@ tasks: HOSTNAME: 127.0.0.1 PORT: "3000" METRICS_PORT: "9090" - run: pnpm dev + launch: + kind: command + exe: pnpm + args: [dev] container: context: app lifecycle: ephemeral @@ -142,7 +173,10 @@ tasks: HOSTNAME: 0.0.0.0 PORT: "3000" METRICS_PORT: "9090" - run: pnpm dev + launch: + kind: command + exe: pnpm + args: [dev] runtime: kind: service listeners: @@ -179,7 +213,13 @@ tasks: Use `ota run clean:start` when you want an ephemeral container lifecycle. Ota injects `OTA_PUBLIC_URL` before the process starts and prints the same URL after startup. test: - context: app + execution: + default_mode: container + modes: + native: + context: host + container: + context: app command: exe: pnpm args: @@ -191,17 +231,26 @@ tasks: - node notes: | Example: `ota run test` before merging from the container context. + verify: + description: Run the complete verification closure in one selected execution mode. + aggregate: + tasks: + - test + safe_for_agent: true agent: entrypoint: setup - default_task: test + default_task: verify safe_tasks: + - setup:host:local - setup - start - clean:start - test + - verify verify_after_changes: - - test + - verify writable_paths: + - .local - node_modules - src - tests @@ -213,7 +262,7 @@ agent: note: Only install ota if it is missing and installation is approved. source: kind: version - version: v1.6.23 + version: v1.6.28 notes: | Use Ota first for setup, validation, and task execution. Treat Ota as the default workflow when the repo runs inside a container. diff --git a/execution/container/node-service/pnpm-lock.yaml b/execution/container/node-service/pnpm-lock.yaml new file mode 100644 index 0000000..9b60ae1 --- /dev/null +++ b/execution/container/node-service/pnpm-lock.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} From 95bd2a443e4559fe792dea18bd4fe0a8f237832e Mon Sep 17 00:00:00 2001 From: bobai Date: Thu, 17 Sep 2026 12:37:48 +0000 Subject: [PATCH 3/4] docs: describe typed Yarn Classic hydration --- reference/task-prepare-package-hydration/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reference/task-prepare-package-hydration/README.md b/reference/task-prepare-package-hydration/README.md index 460a40b..763f8e9 100644 --- a/reference/task-prepare-package-hydration/README.md +++ b/reference/task-prepare-package-hydration/README.md @@ -33,7 +33,9 @@ This example shows the shipped node package-manager `tasks..prepare` slice - `source.kind: node_package_manager` - `source.manager: yarn` - `source.mode: install` -- `source.frozen_lockfile: true` for strict `yarn install --immutable` +- `source.frozen_lockfile: true` for strict modern Yarn `yarn install --immutable`; starting with + the Ota 1.6.28 implementation, a Yarn 1 contract can also declare + `source.yarn_release: classic` for `yarn install --frozen-lockfile` - explicit `requirements.toolchains: [node]` - explicit `effects.writes` - explicit `effects.network_kind: dependency_hydration` From 09ad58d9c043d510f44d9ff35eb0ab814b7fd189 Mon Sep 17 00:00:00 2001 From: bobai Date: Fri, 25 Sep 2026 20:05:31 +0100 Subject: [PATCH 4/4] fix: pin examples bootstrap to released Ota --- ota.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ota.yaml b/ota.yaml index 65dfd46..33ca37c 100644 --- a/ota.yaml +++ b/ota.yaml @@ -150,8 +150,8 @@ agent: ota: note: Only install ota if it is missing and installation is approved. source: - kind: git_rev - rev: 6538cc074a78b3a0471f976a415238d13307a699 + kind: version + version: v1.6.28 notes: | Use Ota first for validation and task execution. Treat the README files as the adoption surface and ota.yaml as the contract.