Skip to content

feat(controller): add PUT /api/v1/humans/{name} route — fixes #729 - #796

Open
hlgone wants to merge 2 commits into
agentscope-ai:mainfrom
hlgone:feat/human-put-route
Open

feat(controller): add PUT /api/v1/humans/{name} route — fixes #729#796
hlgone wants to merge 2 commits into
agentscope-ai:mainfrom
hlgone:feat/human-put-route

Conversation

@hlgone

@hlgone hlgone commented May 11, 2026

Copy link
Copy Markdown

Summary

Adds the missing PUT route for Human resources so the REST API can update an existing Human without forcing DELETE+CREATE.

  • PUT /api/v1/humans/{name} handler + route registration (pattern matching UpdateWorker / UpdateTeam)
  • UpdateHumanRequest partial-update body with nil-vs-[] semantic for slice fields and *int for PermissionLevel to disambiguate "field absent" from "set to 0"
  • hiclaw update human CLI subcommand alongside update worker / update team / update manager
  • HumanResponse now echoes spec-level fields (Email, PermissionLevel, AccessibleTeams, AccessibleWorkers, Note) so read-modify-write callers can avoid raw kubectl

Why

HumanReconciler already supports declarative reconcile via buildDesiredHumanRooms, but no transport surface invoked it on spec changes. hiclaw apply -f human.yaml on an existing Human returned HTTP 405. The only way to change accessibleTeams was DELETE+CREATE, which resets initialPassword and is fragile across BFF / Matrix credential caches.

This is the natural sibling of UpdateWorker / UpdateTeam and the same partial-update + optimistic-lock-retry pattern.

Test plan

5 new tests in hiclaw-controller/internal/server/resource_handler_test.go:

  • TestUpdateHumanAccessibleTeams — PUT new accessibleTeams replaces the slice; unrelated fields preserved
  • TestUpdateHumanRemoveAccessibleTeams — PUT [] clears the slice (revoke-all path)
  • TestUpdateHumanPermissionLevel — PUT permissionLevel replaces value via *int pointer
  • TestUpdateHumanNotFound — PUT non-existent → 404 via writeK8sError
  • TestGetHumanEchoesSpecFields — GET response carries all spec fields

All pass in go test ./internal/server/ against Go 1.25. The unrelated pre-existing TestResolveAndExtract_ZipWithoutSoulmd_Succeeds failure in the golang:1.25 slim image (missing system unzip binary) is not introduced by this PR.

@github-actions

Copy link
Copy Markdown
Contributor

❌ Integration Tests Failed (llm-interaction-2 / mgr=copaw / wk=hermes)

Commit: 959b6e2
Workflow run: #1023

Test Results
No test output captured.
Debug Log (tail)
No debug logs available.

📦 Download full debug logs & test artifacts

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

📊 CI Metrics Report

Summary

Metric Current Baseline Change
LLM Calls 73 86 -13 ↓ -15.1%
Input Tokens 2944203 3825223 -881020 ↓ -23.0%
Output Tokens 15451 19253 -3802 ↓ -19.7%
Total Tokens 2959654 3844476 -884822 ↓ -23.0%

By Role

Role Metric Current Baseline Change
🧠 Manager LLM Calls 60 75 -15 ↓ -20.0%
Input Tokens 2654745 3572217 -917472 ↓ -25.7%
Output Tokens 12614 16994 -4380 ↓ -25.8%
Total Tokens 2667359 3589211 -921852 ↓ -25.7%
🔧 Workers LLM Calls 13 11 +2 ↑ +18.2%
Input Tokens 289458 253006 +36452 ↑ +14.4%
Output Tokens 2837 2259 +578 ↑ +25.6%
Total Tokens 292295 255265 +37030 ↑ +14.5%

Per-Test Breakdown

Test Mgr Calls Wkr Calls Δ Calls Mgr In Wkr In Mgr Out Wkr Out Δ Tokens Trend
02-create-worker 15 0 -2 ↓ -11.8% 541950 0 3376 0 +8755 ↑ +1.6% ✅ improved
03-assign-task 8 7 0 — 0% 356175 153323 1481 1094 -71818 ↓ -12.3% — unchanged
04-human-intervene 11 0 -2 ↓ -15.4% 377231 0 1742 0 -164645 ↓ -30.3% ✅ improved
05-heartbeat 5 0 -2 ↓ -28.6% 225166 0 1132 0 -116065 ↓ -33.9% ✅ improved
06-multi-worker 21 6 -7 ↓ -20.6% 1154223 136135 4883 1743 -541049 ↓ -29.4% ✅ improved

Trends

4 test(s) improved (fewer LLM calls)


Generated by HiClaw CI on 2026-05-11 23:41:18 UTC


📦 Download debug logs & test artifacts

@hlgone

hlgone commented May 11, 2026

Copy link
Copy Markdown
Author

After looking at the job log, the failure is not test-14 itself but the pre-test orchestrator setup failing to verify Higress AI Gateway authorization for the Manager:

[ORCHESTRATOR] Verifying Gateway authorization for Manager...
[ORCHESTRATOR] Gateway returned HTTP 403, retrying... (0s/60s)
… 30 retries …
[ORCHESTRATOR ERROR] Gateway authorization not ready after 60s (HTTP 403)

Preceded by a 300s timeout on Manager welcome-message:

[HiClaw] WARNING: Did not observe the Manager sending its welcome message
(status.welcomeSent=true) within 300s. Installation is still successful,
all services are up — continue with the Element Web instructions below.

So test-14-git-collab.sh never actually ran — orchestrator aborted during the Gateway readiness probe. Both signals (welcome-message timeout + Gateway 403) point at the Higress consumer / Manager-route provisioning race in the CI runner, not anything in this PR's diff (PUT /api/v1/humans/{name} route + humanToResponse spec-field echo).

Matrix evidence still holds — same shard's copaw×copaw and openclaw×openclaw cells passed, only copaw×hermes hit the gateway-auth timeout this run.

Re-running this single matrix cell should be enough to confirm. Happy to push an empty commit if a maintainer prefers that over a manual rerun.

@hlgone

hlgone commented May 14, 2026

Copy link
Copy Markdown
Author

Friendly ping — this PR has been ready for ~3 days. The failing CI cell (llm-interaction-2 / mgr=copaw / wk=hermes) was an orchestrator-side Higress Gateway authorization race during pre-test setup (HTTP 403 for 60s, preceded by a 300s Manager welcome-message timeout), not a regression in this PR's diff. Sister cells in the same shard (copaw×copaw, openclaw×openclaw) passed.

@shiyiyue1102 acknowledged the API gap in #729; the change here is small and confined to the controller's Human route + spec-field echo. Could a maintainer trigger a rerun, or let me know if you'd prefer I push an empty commit to retrigger? Happy to follow whichever convention the project prefers.

hlgone added 2 commits August 3, 2026 10:59
Stock v1.2.0 registers POST/GET/DELETE for humans but no PUT, so
`agt apply -f` against an existing Human fails with 405 Method Not
Allowed (apply.go sends PUT for resources that already exist).
Workers, teams and managers all have the route; humans is the only gap.

Adds the minimal surface to close it, mirroring UpdateWorker:

- UpdateHumanRequest with nil-skip semantics — an omitted field leaves
  the stored value untouched; an empty non-nil slice clears it
- UpdateHuman handler with the same conflict-retry loop as UpdateWorker
- PUT route registration under ActionUpdate authz

Covers accessibleTeams and accessibleWorkers, which are the fields a
partial update needs in order to grant or revoke access without
rewriting the rest of the spec.

Tests pin all three layers: field update, preservation of unsent
fields, and the route table itself (a handler with no route still
answers 405).

Refs agentscope-ai#729
The DTO documents "omitted preserves, empty array clears" but only the
non-empty replace path was covered. Revoking the last team or worker
grant sends [], so a regression there would silently leave access in
place — a security-relevant gap, not a cosmetic one.

The test passes against the current nil-check implementation, so it was
validated by mutation: swapping `!= nil` for `len(...) > 0` (the most
likely wrong implementation) makes it fail with the sets still populated.
Restored afterwards; suite green.
@hlgone
hlgone force-pushed the feat/human-put-route branch from 8cdae1d to 73b4661 Compare August 3, 2026 03:00
@hlgone

hlgone commented Aug 3, 2026

Copy link
Copy Markdown
Author

Rebuilt this PR's head on current main (124f06d) and reduced it to the minimal surface. The previous head was conflicting after the AgentTeams rename, and much of it had become redundant.

What changed vs. the previous version of this PR

before now
diff 398 additions / 6 files 205 additions / 0 deletions / 4 files
CLI (cmd/) modified untouched

Three of the original changes turned out to be unnecessary against current main:

  • The Human GET already echoes accessibleTeams / accessibleWorkers, so the response-echo change is redundant.
  • agt apply -f already sends PUT for resources that already exist (cmd/agt/apply.go), so no CLI change is needed — the generic path works once the route exists.
  • An agt update human subcommand isn't required for the apply -f flow.

What remains is only the server-side gap:

  1. UpdateHumanRequest — nil-skip semantics: an omitted field leaves the stored value untouched; an explicit empty array clears the set.
  2. UpdateHuman handler — mirrors UpdateWorker, including the same conflict-retry loop.
  3. PUT /api/v1/humans/{name} route registration under ActionUpdate authz.

Why this matters: workers, teams and managers all register PUT; humans is the only resource without it. Because agt apply -f sends PUT for existing resources, applying a partial Human spec fails:

$ agt apply -f human-partial.yaml
Error: update human/alice: HTTP 405: Method Not Allowed

That makes it impossible to grant or revoke a Human's team/worker access without rewriting the whole spec.

Tests (4, all in resource_handler_test.go):

  • partial update replaces accessibleWorkers and preserves displayName / email / permissionLevel / accessibleTeams / note
  • partial update replaces accessibleTeams and preserves accessibleWorkers
  • explicit [] clears both sets — distinct from omitting them; this is the revoke path
  • the route table itself: a registered handler with no route still answers 405, so the route is asserted directly

go build ./..., go vet ./... and go test ./... all pass on top of 124f06d (20 packages, 0 failures).

Also verified end to end against a v1.2.0 embedded image with only the controller binary replaced: the partial-spec agt apply -f that previously returned 405 now succeeds, unsent fields (including email / note, which the CLI doesn't echo — checked directly against the API) are preserved, and an explicit [] clears the sets.

Happy to split the tests out or adjust the DTO surface if you'd prefer a different shape.

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