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
18 changes: 12 additions & 6 deletions docs-site/src/content/docs/guides/claude-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,18 @@ here to run.
computer](/guides/public-share/) — a machine another Waired user is lending
out, rather than one of yours. It appears only once you have turned Public
Share on and accepted its warning; until then it is simply not in the list.

It does not change the setting you made there. If you set Public Share to use
another person's machine only when it is better than your own, that still
applies, so this entry can decline — and when it does it says which of the two
reasons it was: no public machine was reachable, or none was better than what
you already have.
It can still be *chosen* on a computer that does not offer it: a session keeps
whichever entry you last picked, and an old one may still hold the name this
entry used to have.

It does not change the settings you made there — it uses them, so this entry
can decline. When it does, it says which of your settings declined and the
command that changes that one: Public Share is off here, it is off for
main-agent or sub-agent turns, or every machine on offer runs a model smaller
than the smallest you accept. Two of the reasons are not about your settings
at all: nobody is lending a machine right now, or — if you set Public Share to
use another person's machine only when it is better than your own — none of
them is.

**Your other computers are listed by name too.** Below the entries above,
`/model` carries a row for each computer currently able to answer, named after
Expand Down
17 changes: 11 additions & 6 deletions docs-site/src/content/docs/ja/guides/claude-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,17 @@ Claude Code を接続したあとに表示され、一度 Claude Code を再起
**Waired public share** は同じ考えを[他人のコンピュータ](/ja/guides/public-share/)に
広げたものです。自分のマシンではなく、ほかの Waired ユーザーが貸し出しているマシンを
使います。この項目は Public Share を有効にして警告に同意したあとにだけ出ます。
それまでは一覧に現れません。

そこで設定した内容を上書きすることはありません。「自分のマシンより良いときだけ他人の
マシンを使う」設定にしているなら、それはそのまま効くので、この項目を選んでも見送られる
ことがあります。見送られたときは、2 つの理由のどちらだったかを伝えます。届く
パブリックマシンが 1 台も無かったのか、いま持っているものより良いマシンが無かったのか、です。
それまでは一覧に現れません。ただし、この項目を出していないコンピュータでも**選ばれる
ことはあります**。セッションは最後に選んだ項目を持ち続けますし、古いセッションは
この項目の以前の名前をまだ持っていることがあります。

そこで設定した内容を上書きすることはありません。むしろその設定を使うので、この項目を
選んでも見送られることがあります。見送られたときは、**自分のどの設定が見送ったのか**と、
それを変えるコマンドを伝えます。このコンピュータで Public Share を切っている、
メイン会話またはサブエージェントのターンについて切っている、貸し出されているマシンが
どれも受け入れる最小のモデルより小さい、のいずれかです。自分の設定とは関係のない理由も
2 つあります。いま貸してくれているマシンが 1 台も無い場合と、「自分のマシンより良いときだけ
他人のマシンを使う」設定にしていて、良いマシンが 1 台も無い場合です。

**ほかのコンピュータは名前でも並びます。** 上の項目の下に、いま応答できる
コンピュータごとに 1 行ずつ、コンピュータの名前を行名に、動かしているモデルを
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/ja/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,8 @@ Claude Code の中で `API Error: 400` と、何が答えられなかったか
| `The computer this turn is pinned to, <名前>, is not answering.` | `waired worker` で固定したそのパソコンが、電源オフ・スリープ・共有オフのいずれか。 | → [パソコンを固定したらリクエストが通らなくなった](#requests-stopped-working-after-i-pinned-a-computer) |
| `The peer <名前> stopped answering after <時間>.` / `The peer <名前> stopped working on this request after <時間>.` | 前者は、答えていたパソコンが応答しなくなった。後者は、止まったと報告したか、エンジンは動いているのに答えなくなった — 固まっているだけで、パソコンが消えたわけではない。 | `waired peers list` と、そのパソコンでの `waired doctor` で確認する。 |
| ``No computer on Waired runs a medium model or larger. Change the floor with `waired worker set --min-model-size`.`` | 自分で設定したルーティングの下限が、このパソコンを含む全パソコンを除外した。 | 下限を下げるか外す → [`--min-model-size`](/ja/reference/cli/#setting-a-minimum-model-size) |
| `Waired public share declined this turn:` に続いて `this computer is set not to use other people's public machines` / `Public Share has not been turned on here` / `Public Share is turned off for … turns` / `no public machine runs …, which is the smallest you accept`。 | 自分の Public Share の設定が見送った。この項目は、もう出していないパソコンでも選ばれることがある。 | 変えるコマンドはメッセージが名指しする。`waired public status` でこれらの設定をまとめて確認し、`waired public use` で変更する。 |
| `Waired public share declined this turn:` に続いて `no public machine is reachable right now` / `Public Share is set to use another machine only when it beats this one, and none does`。 | いま使えるマシンを貸してくれている人がいないか、どれも自分のマシンより良くない。どちらも故障ではない。 | 待つか、`/model` で別の項目を選ぶ。後者を効かなくするには `waired public use --explicit`。 |

たいていはフッターが先に伝えます。赤い `⚠ waired: Waired cannot answer (local
disabled, no peer)` は、自分のどのパソコンも次のターンを受けられないと Waired が
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,8 @@ and send again. The start of the message says which fix applies:
| `The computer this turn is pinned to, <name>, is not answering.` | You pinned that computer with `waired worker` and it is off, asleep or not sharing. | See [Requests stopped working after I pinned a computer](#requests-stopped-working-after-i-pinned-a-computer). |
| `The peer <name> stopped answering after <time>.` / `The peer <name> stopped working on this request after <time>.` | The first: that computer was answering and went quiet. The second: it reported that it had stopped, or its engine is still running but has stopped answering — wedged or frozen, not gone. | Check it with `waired peers list`, and `waired doctor` on that computer. |
| ``No computer on Waired runs a medium model or larger. Change the floor with `waired worker set --min-model-size`.`` | Your own routing floor excluded every computer, this one included. | Lower or clear the floor: [`--min-model-size`](/reference/cli/#setting-a-minimum-model-size). |
| `Waired public share declined this turn:` followed by `this computer is set not to use other people's public machines`, `Public Share has not been turned on here`, `Public Share is turned off for … turns`, or `no public machine runs …, which is the smallest you accept`. | Your own Public Share settings declined it. The entry can still be chosen on a computer that no longer offers it. | The message names the command. `waired public status` shows all of these settings at once; `waired public use` changes them. |
| `Waired public share declined this turn:` followed by `no public machine is reachable right now` or `Public Share is set to use another machine only when it beats this one, and none does`. | Nobody is lending a machine you can use right now, or none of them beats your own. Neither is a fault. | Wait, or pick a different entry in `/model`. To stop the second one applying, `waired public use --explicit`. |

The footer usually says it first. `⚠ waired: Waired cannot answer (local
disabled, no peer)` in red means Waired already knows nothing of yours can
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
status: accepted
---

# Public Share の項目は自分の拒否に自分で答える (20260906 04:10)

## Status
Accepted

## Context

`/model` の **Waired public share** を選んだターンが断られたとき、返る文は 3 通りしか
無かった(`internal/router/public_candidates.go` の `peerOnlyMissNote`)。一方
`publicGateFor` は 4 つの原因を同じゼロ値に潰していた —— policy 未配線 / モード off /
未同意 / トラフィッククラスのトグル off。

結果として、運用者自身が Public Share を切っているホストで返っていたのは
`no public machine is reachable right now` だった。posture が off のあいだ grant
取得器は保持中の grant をすべて解放する(`cmd/waired-agent/public_grants.go`)ので、
地図に provider が 1 台も居らず、到達性の枝に落ちる。曖昧なのではなく**偽**である。

公開 docs は「この項目は見送ることがあり、そのとき 2 つの理由のどちらだったかを伝える」
と約束していた。off はその 2 つのどちらでもない第 3 の状態で、しかも 1 つ目を誤って
名乗っていた(waired-agent#1201、rc5 所見 F7、record waired-ai/waired#1309)。

加えて 2 点:

- 断り文は `No mesh peer is available (routing=public-share-only: …); local state="ready".`
の**括弧の中**に入っていた。見出しは mesh の話で、`routing=` と `local state=` は
Waired の内部語であり、しかも `local state` は**この経路が使わないと決めた機械**の状態。
- 運用者の `waired worker set --min-model-size` がこのホストのエンジンを落としている場合、
`SizeFloorError` の包みが先に立ち、ゲートウェイは `waired_model_too_small` を名乗って
そのコマンドを案内していた。public 専用ターンはローカルエンジンを最初から使わない。

## Decision

1. **4 つの原因を言い分ける。** `publicGate` に `denial` を持たせ、`publicGateFor` が
どのスイッチで断ったかを記録する。**同意をモードより先に見る** ——
`agentconfig.PublicUse.EffectiveMode` が未同意を既に off に畳んでいるため、
逆順だと未同意が永久に名乗れない。理由は**その試行が実際に使った gate** から取る
(policy は atomic pointer で差し替わるので、記録と出力の間に設定変更が挟まると
原因でない posture を説明してしまう)。
2. **設定の枝を到達性の枝より先に置く。** これが欠陥そのもの。到達性は auto の比較より
先のまま(貸し手ゼロなら比較は走っていない)。
3. **消費側のサイズ下限を別に数える。** `PublicPolicy.MinModelSize` は
`waired public use --min-model-size`、運用者の `Inputs.MinModelSize` は
`waired worker set --min-model-size`。別の設定・別のコマンドなので `belowFloor` に
混ぜない。`SizeFloorError` の起動条件にも入れない。
4. **文言にコマンド名を入れる**(オーナー裁定 2026-09-06)。語彙は
`waired public status` が既に出しているもの(`Use public nodes` / `Consented` /
`Smallest model accepted` / `Main agent` / `Sub agents`)だけを使い、新語を作らない。
5. **public 専用の見出しを立てる。** `ModelNotReadyError` に `PublicShare` を足し、
`Error()` は `Waired public share declined this turn: <理由>` を返す。
`routing=` と `local state=` はこの経路からのみ消える —— waired-agent#828 が
local state を入れたのは別の枝で、そちらは変えない。
6. **public 専用ターンでは運用者の routing floor で包まない**(オーナー裁定 2026-09-06)。
waired-agent#1128 の「floor 最優先」をこの 1 ケースだけ狭める。ローカルエンジンに
かかる下限は、ローカルを使わないと決めたターンの拒否理由ではない。

## Consequences

- `/model` の public 行を選んで断られた人は、自分のどの設定が断ったかと、それを
変えるコマンドを 1 文で受け取る。
- 公開 docs の「2 つの理由」は事実でなくなるので、`guides/claude-code.mdx`(+ja)を
書き替える。docs は製品文字列を逐語で引く規則なので、両者は同じ PR で動く。
- `waired-agent#1128` の裁定は生きているが、public 専用ターンという例外を持つ。
この 2 つの順序は `internal/router/public_refusal_note_test.go` の
`TestPublicShareRefusal_OutranksTheOperatorFloor` が固定する。
- 未読の `public_use.json` も「未同意」と名乗る(ゼロ値の policy が publish される
ため)。daemon 側は真因をログに出しており、行き先のスイッチはどちらでも同じなので
記録するにとどめる。

## Refs
- https://github.com/waired-ai/waired-agent/issues/1201
- https://github.com/waired-ai/waired-agent/issues/1252
- docs/decisions/20260820/0500-public-share-in-the-picker-narrows-only.md
11 changes: 8 additions & 3 deletions internal/gateway/anthropic_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,14 @@ const (
// It does NOT override the consumer's standing Public Share posture
// (owner ruling 2026-08-20): with the posture on `auto`, a public
// machine still has to beat this host's own best tier, so the entry can
// legitimately decline. It is offered only on a host that has consented
// and enabled Public Share, so the case "offered but the posture forbids
// everything" does not arise.
// legitimately decline.
//
// The picker leaves the row out on a host that has not enabled Public
// Share, but "offered but the posture forbids everything" DOES arise
// and this comment used to deny it (waired-agent#1201): a session keeps
// the id it last picked in its own settings, and the legacy spelling
// below is still routed for exactly that reason. The router words those
// refusals by naming which switch declined.
ModelWairedPublic = "waired/public"
)

Expand Down
9 changes: 7 additions & 2 deletions internal/gateway/anthropic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,10 @@ func TestAnthropicMessages_ModelNotReadyMaps503Overloaded(t *testing.T) {
// "downloading" is the point of the fixture — a model on its way keeps
// the retryable shape (waired-agent#788), so the state has to be on the
// error rather than only in the wrapped message.
sel := &fakeSelector{err: &router.ModelNotReadyError{ModelID: "waired/default", State: "downloading"}}
// LocalArrivalAnswers: a LOCAL branch produced this, so the download really
// is what the client would be waiting for (waired-agent#1252).
sel := &fakeSelector{err: &router.ModelNotReadyError{
ModelID: "waired/default", State: "downloading", LocalArrivalAnswers: true}}
gw := anthropicGatewayUnderTest(t, sel, "http://unused")
body := `{"model":"waired/default","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}`
r := httptest.NewRequest(http.MethodPost, "/anthropic/v1/messages", bytes.NewBufferString(body))
Expand Down Expand Up @@ -772,7 +775,9 @@ func TestAnthropicMessages_MappedModelNotReady503(t *testing.T) {
// must win over any blanket no-model shape.
sel := &scriptedSelector{errs: []error{
wrap(router.ErrModelNotFound, "alias claude-fable-5[1m] not found"),
&router.ModelNotReadyError{ModelID: "qwen3-8b-instruct", State: "downloading"},
// LocalArrivalAnswers: a LOCAL branch produced this (waired-agent#1252).
&router.ModelNotReadyError{
ModelID: "qwen3-8b-instruct", State: "downloading", LocalArrivalAnswers: true},
}}
gw := anthropicGatewayWithResolver(t, sel, "http://unused", func(string) (string, bool) {
return "qwen3-8b-instruct", true
Expand Down
5 changes: 4 additions & 1 deletion internal/gateway/openai_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ func TestOpenAIChatCompletions_ModelNotReady503(t *testing.T) {
// The fixture says "downloading", so it must carry that state: since
// waired-agent#788 the retryable shape is reserved for a model on its
// way, and the bare sentinel no longer stands in for one.
sel := &fakeSelector{err: &router.ModelNotReadyError{ModelID: "waired/default", State: "downloading"}}
// LocalArrivalAnswers: a LOCAL branch produced this, so the download really
// is what the client would be waiting for (waired-agent#1252).
sel := &fakeSelector{err: &router.ModelNotReadyError{
ModelID: "waired/default", State: "downloading", LocalArrivalAnswers: true}}
gw := newGatewayUnderTest(t, sel, "http://unused")
body := `{"model":"waired/default","messages":[]}`
r := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", bytes.NewBufferString(body))
Expand Down
Loading
Loading