Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ jobs:
- 'scripts/tests/test-coroot-helper-image-pull-policy.sh'
- 'scripts/tests/test-agentic-engineer-schedule.sh'
- 'scripts/tests/test-agentic-engineer-lane-store.sh'
- 'scripts/tests/test-agentic-engineer-lane-store-lease.sh'
- 'scripts/tests/test-kagent-claude-harness-wrapper.sh'
- 'scripts/kagent-claude-harness/**'
- 'scripts/tests/test-anthropic-proxy-envoy-config.sh'
Expand Down Expand Up @@ -2886,6 +2887,16 @@ jobs:
shellcheck scripts/tests/test-agentic-engineer-lane-store.sh
bash scripts/tests/test-agentic-engineer-lane-store.sh

- name: 🔒 Validate the Agentic Engineer lane store lease
if: needs.changes.outputs.k8s == 'true'
# Runs the store's pinned rclone image with its own arguments: the lease a
# run takes on memory/ refuses every other run, even many at once, the
# store refuses a write there without its token, and after a restart the
# former holder can neither write nor release.
run: |
shellcheck scripts/tests/test-agentic-engineer-lane-store-lease.sh
bash scripts/tests/test-agentic-engineer-lane-store-lease.sh

- name: 🧠 Validate the kagent harness plugin wrapper
if: needs.changes.outputs.k8s == 'true'
# Runs the image's real `claude` wrapper against a stub Claude Code: a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
#
# - webdav (8080): read and write, behind the lane-store credential. Only
# Substrate's egress gateway reaches it, sending that credential in place of
# the agent's placeholder (egress-policy-lane-store.yaml).
# the agent's placeholder (egress-policy-lane-store.yaml). It also holds
# the lane's single-writer lease, a WebDAV LOCK on memory/ kept only in
# its memory, and refuses a write under memory/ without that lock's token
# (devantler-tech/monorepo#4910). Lock tokens count up from the second the
# store started, so a run trusts one only once it is a minute behind the
# store's clock; this relies on node clocks agreeing within a minute. A run
# that dies before releasing the lease leaves it held, and every later run
# stops at it; restarting this Deployment frees it.
# - reader (8081): read only, with no credential of its own. Only the
# Kubernetes API server reaches it, so a reader needs the
# services/proxy grant in role-lane-store-reader.yaml.
Expand Down
64 changes: 53 additions & 11 deletions k8s/providers/hetzner/apps/agentic-engineer/job-schedule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,26 +97,68 @@ spec:
Scheduled run of the devantler-tech Agentic Engineer on its kagent lane:
instance `kagent-cluster`, writer namespace `kagent/*`, one run a day.

1. Restore your native memory from the lane store. List memory/ with a
WebDAV PROPFIND (`curl -fsS -X PROPFIND -H 'Depth: 1'` plus the store
header) and download every file into ~/lane-memory/. Only a 404 for
memory/ means a first run, so start with an empty ~/lane-memory/. Any
other failure means the store is unreachable: stop and report it.
2. Shallow-clone devantler-tech/monorepo, read its AGENTS.md, load the
1. Take the lane store's single-writer lease: an exclusive WebDAV lock
on memory/ that also fences every write under it, so that overlapping
runs never overwrite each other's memory (devantler-tech/monorepo#4910).
Read every status with `curl -sS -D -`, not -f, which hides it.
a. Send a PROPFIND to memory/ with the store header and `Depth: 0`.
A 404 means a first run: create memory/ with a MKCOL. Any reply
but 207 or that 404 stops the run.
b. Send a LOCK to memory/ with the store header, `Timeout: Infinite`
and `Content-Type: application/xml`, and this body with OWNER
replaced by `agentic-engineer`, your UTC start time and a random
16-hex-digit nonce, which is your owner:
`<?xml version="1.0"?><D:lockinfo xmlns:D="DAV:"><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype><D:owner>OWNER</D:owner></D:lockinfo>`
A 200 means you hold the lease: keep the reply's Lock-Token value,
angle brackets included. A 423 means another run holds it, or a
run that crashed left it behind: stop, upload nothing and report
it. Never remove a lease you did not take. Any other reply also
stops the run before it touches memory.
c. The store numbers its tokens up from the second it started, and an
UNLOCK checks only the token, so a restarted store could hand your
token to another run whose lease your UNLOCK would then release.
Your token is safe once its number is at least 60 below a reply's
Date header in Unix seconds: no restart can reissue it then, since
the cluster keeps its nodes' clocks within a minute of each other.
Until it is safe, never UNLOCK it. If the LOCK reply shows it is not yet
safe, the store started moments ago: wait out the difference plus
a second, if that is under 10 minutes, then refresh the lease as in
step 4 and check again. If it is still not safe, or the refresh
fails or names another owner, upload nothing, leave the lease
alone and report it.
2. Restore your native memory from the lane store: list memory/ with a
PROPFIND (`-X PROPFIND -H 'Depth: 1'` plus the store header), list
each folder it shows the same way, and download every file into
~/lane-memory/, which stays empty on a first run. A listing or
download you cannot complete stops the run: release the lease with
the UNLOCK of step 4, upload nothing and report it.
3. Shallow-clone devantler-tech/monorepo, read its AGENTS.md, load the
agentic-engineering plugin and invoke its agentic-engineer entrypoint
as this scheduled run. ~/lane-memory/ is your native memory (the
legacy layout), as the durable-memory guide says for this lane.
Follow the consumer contract and fail closed on missing deployment
facts. Do not duplicate the role here.
3. Before you stop, however the run ended, upload ~/lane-memory/ back to
memory/ on the lane store, and upload this session's transcript (the
newest Claude Code session JSONL under your Claude config directory)
and your run report to runs/<UTC date>/transcript.jsonl and
4. Before you stop, however the run ended, refresh the lease: a LOCK to
memory/ with the store header, `Timeout: Infinite`, no body, and your
Lock-Token value in parentheses as the If header (`If: (<t>)` for
`Lock-Token: <t>`). You still hold the lease only if the reply is 200
and its owner is yours. On any other reply, upload nothing, leave the
lease alone and report it. Otherwise upload ~/lane-memory/ back to
memory/ one file at a time, with that same If header on every PUT and
on the MKCOL of any folder inside memory/: the store refuses a write
under memory/ that lacks the lease's token, and a second write with
one token while the first is still running. A 412 or 423 means you
lost the lease: stop uploading, leave the lease alone and report it.
Then upload this session's transcript (the newest
Claude Code session JSONL under your Claude config directory) and your
run report, with no If header, to runs/<UTC date>/transcript.jsonl and
runs/<UTC date>/report.md. If an earlier run that day already wrote
them, never replace them: write yours beside them as
transcript-<UTC time HHMMSS>.jsonl and report-<UTC time HHMMSS>.md.
Create a missing folder with MKCOL before writing into it, and check
that every upload succeeded.
that every upload succeeded. Last, when every write under memory/
succeeded, release the lease: an UNLOCK to memory/ with the store
header and your Lock-Token value as the Lock-Token header.

This run may open, update and review devantler-tech pull requests and
issues, per AGENTS.md. It never merges. Only when genuinely blocked on a
Expand Down
Loading
Loading