fix(assistant): 人在現場那一路的根目錄解析帶上文件記載的預設值
技能本文寫著那一行「涵蓋了文件記載的 ~/.jsc 退讓」,實際上沒有。JSC_HOME 沒設時 $JSC_HOME/current 展開成 /current,不是退讓到任何地方——殼並不知道 那個變數的文件預設值是什麼。解析式現在自己帶預設,退讓才真的發生。 這台機器從頭到尾沒設過那個變數,所以每一次人在現場叫用都停在第 0 步,而且 守衛開的藥方指向不相干的地方:它說根目錄不見了、叫人跑部署,但根目錄好好 的,壞的是解析式自己。排程那一路不受影響,條目自己帶著變數值。 目錄檢查留著,理由改寫進本文:解析式交出一條看起來合理卻不存在的路徑, 不是只有變數沒設一種成因——變數指到已經刪掉的目錄、精簡環境裡連家目錄都 沒設、連結農場根本還沒部署,三種都一樣過得了前三道關。 行為清單那一份早就寫對了,是技能本文漂走。這一次把兩份對齊,並把事故記述 改成過去式,免得下一輪有人照著它重寫回錯的形狀。 實測表第四列不動:那是量到的結果,而且新寫法一樣帶變數、無人值守一樣被擋, 另外加一段講明預設值改的是解析出哪條路徑,不是誰可以跑它。 三份 manifest 版號 0.2.0 升到 0.2.1。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,17 +24,21 @@ The root comes from outside this skill. `schedule.sh` resolves it while a person
|
||||
| Who invoked this round | Where `{CURRENT}` comes from |
|
||||
| --- | --- |
|
||||
| the schedule — an unattended round, the `patrol` whose trigger is 排程 | the path after `工具根目錄=` in the invocation text, taken verbatim. No command is run |
|
||||
| a person, in front of the terminal | the same token when the invocation carries one; otherwise `readlink -f "$JSC_HOME/current"`, run once |
|
||||
| a person, in front of the terminal | the same token when the invocation carries one; otherwise `readlink -f "${JSC_HOME:-$HOME/.jsc}/current"`, run once |
|
||||
|
||||
**An unattended round that finds no root in its invocation stops there.** Report that the scheduled entry carries no `工具根目錄=` — an entry written by an older `schedule.sh` — say the fix is to run `start` again, or `jsc-assist/tools/schedule.sh install patrol` under `$JSC_HOME/current`, so the entry is rewritten with the root in it. Then take the operation's `aborted` status, write the `skill-end`, and stop.
|
||||
|
||||
Never work the root out instead. `readlink -f "$JSC_HOME/current"`, `ls -d "$JSC_HOME/current"` and every other resolve are refused in an unattended session — measured, see the table below — so running one does not produce a root, it produces a round that stops one step earlier having recorded nothing. Never fall back to `$JSC_HOME/current` as a written-out path either, never take a path from the plugin prompt or a previous transcript, and never guess.
|
||||
Never work the root out instead. `readlink -f "${JSC_HOME:-$HOME/.jsc}/current"`, `ls -d "$JSC_HOME/current"` and every other resolve are refused in an unattended session — measured, see the table below — so running one does not produce a root, it produces a round that stops one step earlier having recorded nothing. Never fall back to `$JSC_HOME/current` as a written-out path either, never take a path from the plugin prompt or a previous transcript, and never guess.
|
||||
|
||||
**Only an attended invocation may resolve the root itself.** `readlink -f "$JSC_HOME/current"` covers the documented `~/.jsc` fallback in the same call and prints one literal absolute path. It raises one permission prompt, and a person is there to answer it once. That is the whole reason the branch exists: portability survives where somebody can approve it, and nowhere else.
|
||||
**Only an attended invocation may resolve the root itself.** `readlink -f "${JSC_HOME:-$HOME/.jsc}/current"` prints one literal absolute path. It raises one permission prompt, and a person is there to answer it once. That is the whole reason the branch exists: portability survives where somebody can approve it, and nowhere else.
|
||||
|
||||
**The `:-$HOME/.jsc` half is load-bearing, and dropping it is how this line was wrong before.** `$JSC_HOME` is documented as defaulting to `~/.jsc`, and the shell does not know that: with the variable unset, a bare `$JSC_HOME/current` expands to `/current`, which is not a fallback to anything. Write the default into the expansion or the documented default never happens.
|
||||
|
||||
Take the root once per invocation and reuse that one answer. Never resolve it again per call, never print it as a report line of its own, and never add a tool that prints it. Never test the root with a command either — an unattended round cannot, and the first script call is the test that matters anyway.
|
||||
|
||||
An empty token, an empty `readlink` result, a path that is not absolute, or a resolved path that is not an existing directory means there is no root to work with. **That fourth item is the one the other three wave through.** With `JSC_HOME` unset, `readlink -f "$JSC_HOME/current"` prints `/current` and exits 0 — non-empty, absolute, and past every other item — and each literal path built from it then names a place that is not there. So the attended resolve is only accepted once `[ -d "{the path just printed}" ]` says that directory exists, run in the same approved step as the resolve itself. The unattended round tests nothing, exactly as above: its root was written into the entry by whoever installed the schedule, and its first script call is what fails if that root is wrong. Report it, say `jsc-cli:deploy` has to run, take the operation's `aborted` status, and stop. Never fall back to a cache path, and never create the root here. Completion condition: one literal absolute path is in hand and every later command line carries it, or the missing root was reported and the operation stopped.
|
||||
An empty token, an empty `readlink` result, a path that is not absolute, or a resolved path that is not an existing directory means there is no root to work with. **That fourth item is the one the other three wave through**, so the attended resolve is only accepted once `[ -d "{the path just printed}" ]` says that directory exists, run in the same approved step as the resolve itself. It stays a separate check even now that the expansion carries its own default, because the ways a resolve can print a plausible path that is not there do not end with an unset variable: `JSC_HOME` set to a directory that no longer exists, `HOME` unset in a stripped environment, or the link farm simply never deployed all reach this line with something absolute in hand.
|
||||
|
||||
**How that guard earned its place is worth keeping.** This step used to resolve a bare `$JSC_HOME/current`, and on a machine that had never set `JSC_HOME` — the documented default being `~/.jsc` — it printed `/current` and exited 0: non-empty, absolute, and past every other item. The directory check was the only thing that caught it, and it caught it while pointing at the wrong culprit, because a resolve that cannot be trusted looks exactly like a root that is not there. The expansion above is the fix; this check is what noticed. The unattended round tests nothing, exactly as above: its root was written into the entry by whoever installed the schedule, and its first script call is what fails if that root is wrong. Report it, say `jsc-cli:deploy` has to run, take the operation's `aborted` status, and stop. Never fall back to a cache path, and never create the root here. Completion condition: one literal absolute path is in hand and every later command line carries it, or the missing root was reported and the operation stopped.
|
||||
|
||||
## Every script call carries a literal absolute path
|
||||
|
||||
@@ -57,6 +61,8 @@ The reason is the permission layer: it matches its rules against the command tex
|
||||
|
||||
A literal allow rule that itself starts with `$JSC_HOME` was added to the settings file and the same call was still refused, so no permission rule makes the variable form work either. The literal path is the whole fix, on both sides.
|
||||
|
||||
Row 4 was measured before the resolve carried its own default, and the verdict is the same for `readlink -f "${JSC_HOME:-$HOME/.jsc}/current"`: the rule is matched against the text as written, and that text still holds variables. The default fixed which path the resolve produces, not whether an unattended round may run it — that round still gets its root handed in, and this line stays the attended branch only.
|
||||
|
||||
**These rules outrank portability, and the next maintainer is the one who has to know why.** A variable in the path reads as the portable choice and costs nothing while a person is watching: the prompt appears, somebody approves it, the round carries on. The scheduled round has nobody to approve it. It stops at its first script call, records nothing, writes no heartbeat, and the machine then reads as a stopped assistant with no trace of the refusal anywhere. And the resolve is no way out of that, because row 4 of the table is the resolve itself: a round that cannot run a script cannot run the command that would have told it which script to run. That is why the root is handed in by whoever installed the schedule, and why anything written into a command line here is already literal.
|
||||
|
||||
## Tool paths
|
||||
|
||||
Reference in New Issue
Block a user