feat(harness): repo-readonly/v1 + gitea-ssh layer in workflow-script/v1; agent_repo on test-validate/frozen-fixture-emit (WF-COMP-34, bug 3bee78fa/a3ba44bc)
This commit is contained in:
@@ -21,4 +21,16 @@ layers:
|
|||||||
# on the entrypoint process environment before shell_runner runs (entrypoint reads
|
# on the entrypoint process environment before shell_runner runs (entrypoint reads
|
||||||
# os.environ via env=os.environ.copy() in subprocess.run at entrypoint/runners/shell.py:73,
|
# os.environ via env=os.environ.copy() in subprocess.run at entrypoint/runners/shell.py:73,
|
||||||
# so the same credential channel an agent node gets reaches a script node unchanged).
|
# so the same credential channel an agent node gets reaches a script node unchanged).
|
||||||
- context: cp-service-token/v1
|
- context: cp-service-token/v1
|
||||||
|
# spec/typed-workflows.md WF-COMP-34 (bug 3bee78fa, 2026-10-02) — read-only
|
||||||
|
# project-repo clone for harness-path kind: script nodes. Layered LAST so
|
||||||
|
# its clone runs after the two AU-51 wiring layers and before the script
|
||||||
|
# starts (init.sh order: shared-variables -> cp-service-token -> repo-readonly).
|
||||||
|
# When AGENT_REPO_URL is unset (the pre-fix runtime, or any template that
|
||||||
|
# does not declare agent_repo) this layer is a no-op; the V2 guard
|
||||||
|
# (entrypoint/entrypoint.py:901-906) does NOT trip because the unset case
|
||||||
|
# is a no-op there too. Layered-in instead of layering agent-repo/v1
|
||||||
|
# because that context's fork management and push side effects are wrong
|
||||||
|
# for a read-only script node (see bug item body).
|
||||||
|
- context: gitea-ssh/v1
|
||||||
|
- context: repo-readonly/v1
|
||||||
|
|||||||
24
harnesses/contexts/repo-readonly/v1/harness.yaml
Normal file
24
harnesses/contexts/repo-readonly/v1/harness.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
kind: context
|
||||||
|
name: repo-readonly
|
||||||
|
version: 1
|
||||||
|
description: "Read-only project-repo clone for harness-path kind: script nodes (spec/typed-workflows.md WF-COMP-34, bug 3bee78fa). Clones AGENT_REPO_URL (the upstream project repo URL emitted by the WF-COMP-34 compiler emission as payload.agent_repo.url and propagated to the container env by the dispatcher) into /workspace/project at its default branch. No fork management, no AR-39 select-base, no scratchpad directory, no .gitignore munging, no finalize.sh push. The script node reads repo files only; it never writes back through a fork. Distinct from agent-repo/v1 (the agent-node branch-per-task fork-management context) on every one of those axes."
|
||||||
|
requires: []
|
||||||
|
provides: []
|
||||||
|
|
||||||
|
# WF-COMP-34: this layer's init.sh is the read-only clone. It only runs
|
||||||
|
# when AGENT_REPO_URL is set; the unset case is a no-op (a script node
|
||||||
|
# whose template omits agent_repo keeps its pre-fix silent empty workspace
|
||||||
|
# runtime exactly, verified by
|
||||||
|
# tests/test_entrypoint.py::TestV2WorkspaceAssertions::test_v2_no_agent_repo_url_skips_workspace_check).
|
||||||
|
#
|
||||||
|
# AGENT_WORKING_DIR=/workspace/project is the env var
|
||||||
|
# entrypoint.py:_apply_working_dir reads (entrypoint/entrypoint.py:908-916),
|
||||||
|
# and the path entrypoint/runners/shell.py:67 sets as the subprocess cwd
|
||||||
|
# when context["workspace"] is unset and no clone action ran.
|
||||||
|
env:
|
||||||
|
AGENT_WORKING_DIR: /workspace/project
|
||||||
|
|
||||||
|
scripts:
|
||||||
|
init: "./init.sh"
|
||||||
|
|
||||||
|
secrets_required: []
|
||||||
79
harnesses/contexts/repo-readonly/v1/init.sh
Executable file
79
harnesses/contexts/repo-readonly/v1/init.sh
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# repo-readonly init — read-only project-repo clone for harness-path
|
||||||
|
# kind: script nodes (spec/typed-workflows.md WF-COMP-34, bug 3bee78fa).
|
||||||
|
#
|
||||||
|
# Threat model: the script node is invoked by entrypoint/runners/shell.py
|
||||||
|
# with cwd=context["workspace"]; the existing entrypoint paths set
|
||||||
|
# context["workspace"] to /workspace/project from AGENT_WORKING_DIR
|
||||||
|
# (the env var this init.sh exports via its harness.yaml env field).
|
||||||
|
# The init.sh MUST clone AGENT_REPO_URL (the upstream project repo URL
|
||||||
|
# emitted by the WF-COMP-34 compiler emission as payload.agent_repo.url
|
||||||
|
# and propagated to the container env by the dispatcher) into
|
||||||
|
# /workspace/project at its default branch — read-only, no fork, no push,
|
||||||
|
# no AR-39 select-base, no scratchpad, no .gitignore munging.
|
||||||
|
#
|
||||||
|
# Bug discovered live 2026-10-02 (bug 3bee78fa, run
|
||||||
|
# 9a368e71-d6ab-4185-a98c-9b31d41bb4b5): without this layer, every
|
||||||
|
# harness-path kind: script node ran with /workspace/project missing,
|
||||||
|
# so any preflight that read repo files (e.g. preflight-test-collection
|
||||||
|
# invoking pytest --collect-only tests/test_k8s_gc.py) saw an empty cwd
|
||||||
|
# and either reported zero files or failed with the upstream tool's own
|
||||||
|
# misconfig error. The WF-COMP-34 compiler emission (this repo's
|
||||||
|
# controlplane/typed_workflows/compiler.py, bug 3bee78fa) writes
|
||||||
|
# payload.agent_repo onto every harness-path script node's manifest, so
|
||||||
|
# the URL reaches the env when the template (or the node) declares
|
||||||
|
# agent_repo. This init.sh is the other half — the layer that actually
|
||||||
|
# clones it.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== repo-readonly/v1 init.sh ==="
|
||||||
|
|
||||||
|
# WF-COMP-34 unset-case contract: if AGENT_REPO_URL is unset, this is a
|
||||||
|
# no-op. The script node keeps its pre-fix silent empty workspace
|
||||||
|
# runtime exactly — a template that omits agent_repo does not get a
|
||||||
|
# surprise clone. The entrypoint V2 guard (entrypoint/entrypoint.py:901-906)
|
||||||
|
# only fires when AGENT_REPO_URL IS set; the unset case is a no-op on
|
||||||
|
# both sides (no clone here, no V2 trip there), so the legacy
|
||||||
|
# script_on_harness=False path's observable behaviour is preserved
|
||||||
|
# verbatim.
|
||||||
|
if [ -z "${AGENT_REPO_URL:-}" ]; then
|
||||||
|
echo "AGENT_REPO_URL unset; no clone (WF-COMP-34 unset-case contract)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the clone target already exists (e.g. a previous layer cloned into
|
||||||
|
# it), skip — never clobber. The framework composite order
|
||||||
|
# (shared-variables/v1 first, cp-service-token/v1 second,
|
||||||
|
# repo-readonly/v1 last) guarantees neither prior layer writes here.
|
||||||
|
if [ -d /workspace/project ]; then
|
||||||
|
echo "/workspace/project already exists; skipping clone."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /workspace
|
||||||
|
|
||||||
|
echo "Cloning (read-only): $AGENT_REPO_URL -> /workspace/project"
|
||||||
|
# Read-only clone: no checkout of a task branch, no push target, no
|
||||||
|
# remote operations beyond the initial clone. --depth 1 is enough —
|
||||||
|
# a script node reads files, not history. If history is needed later,
|
||||||
|
# the spec can be amended; today every preflight is a pytest --collect-only
|
||||||
|
# or a prettier --check or similar that does not need commit ancestry.
|
||||||
|
if git clone --depth 1 "$AGENT_REPO_URL" /workspace/project; then
|
||||||
|
echo "Cloned $AGENT_REPO_URL to /workspace/project"
|
||||||
|
else
|
||||||
|
# Loud failure: a harness-path script node that declares agent_repo
|
||||||
|
# and finds the clone fails SHOULD fail the task at init time, not
|
||||||
|
# silently leave /workspace/project missing for the V2 guard to
|
||||||
|
# catch 26 seconds later (that failure mode is the symptom this
|
||||||
|
# bug was filed for).
|
||||||
|
echo "ERROR: Failed to clone $AGENT_REPO_URL" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Strip any symlinks (security: prevent /proc/1/environ exfiltration;
|
||||||
|
# mirrors agent-repo/v1's defence).
|
||||||
|
find /workspace/project -type l -exec rm {} \; 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "AGENT_WORKING_DIR=${AGENT_WORKING_DIR:-/workspace/project}"
|
||||||
|
echo "=== repo-readonly/v1 init.sh complete ==="
|
||||||
@@ -12,6 +12,8 @@ scope_budget:
|
|||||||
files_modified_max: 2
|
files_modified_max: 2
|
||||||
lines_diff_max: 200
|
lines_diff_max: 200
|
||||||
wall_clock_seconds_max: 600
|
wall_clock_seconds_max: 600
|
||||||
|
agent_repo:
|
||||||
|
url: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes.git"
|
||||||
nodes:
|
nodes:
|
||||||
- id: frozen-fixture-emit
|
- id: frozen-fixture-emit
|
||||||
kind: script
|
kind: script
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ scope_budget:
|
|||||||
files_modified_max: 1
|
files_modified_max: 1
|
||||||
lines_diff_max: 50
|
lines_diff_max: 50
|
||||||
wall_clock_seconds_max: 600
|
wall_clock_seconds_max: 600
|
||||||
|
agent_repo:
|
||||||
|
url: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes.git"
|
||||||
nodes:
|
nodes:
|
||||||
- id: preflight-test-collection
|
- id: preflight-test-collection
|
||||||
kind: script
|
kind: script
|
||||||
|
|||||||
Reference in New Issue
Block a user