fix(harness): keep edits beside themselves, never follow symlinks, cut only gstack's own context block

Review blockers on #3526: uninstall deleted the edit backups under the state root;
unregister followed a symlinked folder below the destination; a lone or edited
context marker made the cut eat user lines; a re-register adding --context left an
unrecorded block. Also: ledger written before the files, --refresh-registered no
longer re-runs harness hosts through setup (gstack-harness refresh keeps their mode),
bash goes through runBashSync, and setup tests follow openclaw/hermes becoming installs.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-28 01:40:21 +00:00
1 parent 808dead70e
commit dd2f3c13ec
10 files changed
+200 -71

No files matched your search

+4 -2
View File
@@ -26,8 +26,10 @@ prints every path it wrote and the command that removes them. An explicit
Every file goes into a ledger, `$GSTACK_STATE_ROOT/harness/<id>.ledger`, with
its sha256. Registering again changes only what changed; a folder or skill name
gstack did not write stops it (`HARNESS_UNOWNED_ENTRY`); `gstack-uninstall`
and `gstack-harness unregister` remove exactly the ledger's files and move any
file you edited to `$GSTACK_STATE_ROOT/backups/harness/`. `./setup
and `gstack-harness unregister` remove exactly the ledger's files, never
through a symlink, and rename any file you edited to `<file>.edited-<time>`
beside itself, where uninstall cannot take it. A context block is cut only
when it is the one gstack wrote; an edited block is left for you. `./setup
--refresh-registered` (what `/gstack-upgrade` runs) refreshes every registration.
`gstack-harness status` lists them and runs the harness's own skill list where
it has one (`visible to harness: yes`). Errors: [troubleshooting](troubleshooting.md#agent-harnesses-gstack-harness).
+3 -1
View File
@@ -5077,13 +5077,15 @@ Hermes opens only skills it has indexed, so a router that points at unlisted fil
<a id="harness-unsafe-destination"></a>
### `gstack-harness: refusing to write into <dir>: <why>` (HARNESS_UNSAFE_DESTINATION)
The destination is your home folder, the filesystem root, inside the gstack checkout, a symlink, or a path with a tab or newline. Nothing was written. Pick the harness's own skills folder, for example `--skills-dir ~/.openclaw/skills`.
The destination is your home folder, the filesystem root, inside the gstack checkout, a symlink (or holds one where gstack's own folders were), or a path with a tab or newline. Nothing was written. Pick the harness's own skills folder, for example `--skills-dir ~/.openclaw/skills`.
<a id="harness-unowned-entry"></a>
### `gstack-harness: <path> already exists and gstack did not write it` / `a skill named <name> already exists at <path>` (HARNESS_UNOWNED_ENTRY)
gstack only replaces files its ledger says it wrote. A folder or a skill with the same name is yours (or another tool's), and harnesses resolve skills by name, so gstack stops instead of shadowing it. Rename or move that entry, then register again.
The same code stops `register generic --context <file>` when the file holds a `<!-- gstack:begin harness-routing -->` or `<!-- gstack:end harness-routing -->` line without exactly one partner, or a gstack block someone edited. Delete those marker lines (and the lines between them, once you have kept what you want), then register again.
<a id="harness-render-failed"></a>
### `gstack-harness: could not render <harness> skills: ...` (HARNESS_RENDER_FAILED)