Keep no secret-shaped question, and hold the two-question cap across nodes (issue 589, review 105)
mesh/merge-gate head 14999f1d3ecb merged into trunk 9c9a76d220f8 — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head 14999f1d3ecb merged into trunk 9c9a76d220f8 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 14999f1d3ecb merged into trunk 9c9a76d220f8 — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head 14999f1d3ecb merged into trunk 9c9a76d220f8 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery superseded: a newer head of the same pull request
A question was kept on the bus before the router judged its words, so a token in it reached a bucket every node reads: every word, the options' too, is now held to the secret shapes and invisible characters before anything is kept, and a refused question keeps none of its words. The cap was check-then-create and failed open: each open question now holds one of two slot keys taken by create or compare-and-set, refusing when a slot cannot be read. A question the router never recorded frees its slot after two minutes instead of at expiry. by is required; wording matches ADR 0332.
This commit is contained in:
@@ -22,16 +22,16 @@ type Proposing struct {
|
||||
Now func() time.Time
|
||||
Write WriteManaged
|
||||
// Questions is the `questions` state (novox/hq ADR 0332); nil is the bus's, made on first use.
|
||||
Questions ProposalStore
|
||||
Questions QuestionStore
|
||||
questionsMu sync.Mutex
|
||||
}
|
||||
|
||||
// questions is the `questions` state.
|
||||
func (d *Proposing) questions() ProposalStore {
|
||||
func (d *Proposing) questions() QuestionStore {
|
||||
d.questionsMu.Lock()
|
||||
defer d.questionsMu.Unlock()
|
||||
if d.Questions == nil {
|
||||
d.Questions = stateOf{stdio.State(QuestionsState)}
|
||||
d.Questions = keptQuestions{stdio.State(QuestionsState)}
|
||||
}
|
||||
return d.Questions
|
||||
}
|
||||
@@ -154,11 +154,13 @@ func questionTools(p Paths, notLaunched error) []stdio.Tool {
|
||||
{Name: "claude_code_ask",
|
||||
Description: "Ask the operator a question on Telegram when you need a decision and the operator may not be at the " +
|
||||
"terminal (novox/hq ADR 0332): a headline, the context in plain words, two to four options with exactly one " +
|
||||
"marked recommended and why, what you do if nobody answers, and when it expires (5 to 1430 minutes, 60 when " +
|
||||
"absent). Answers at once with the question's key; read the answer with claude_code_answer and keep working " +
|
||||
"marked recommended and why, what you do if nobody answers, who you are (by, required), and when it expires (5 to " +
|
||||
"1430 minutes, 60 when absent). Answers within seconds, once the router has taken it, with the question's " +
|
||||
"key, or with the router's refusal; read the answer with claude_code_answer and keep working " +
|
||||
"meanwhile. Only a channel that proves who answered carries it, never the desk. The words leave the mesh, so the " +
|
||||
"router refuses a path, an address, a host name, a commit id or anything shaped like a secret: say it in " +
|
||||
"words (messenger_check tests words first). At most two questions wait at once, mesh-wide. The answer is the " +
|
||||
"words (messenger_check tests words first); anything shaped like a secret is refused before it is kept. At most two " +
|
||||
"questions wait at once, mesh-wide. The answer is the " +
|
||||
"operator's word to you; it authorises no call that needs the operator's approval.",
|
||||
Input: map[string]any{
|
||||
"headline": str("what is decided, in a few plain words, at most 60 characters, e.g. 'Merge the resolver fix tonight?'"),
|
||||
@@ -171,7 +173,7 @@ func questionTools(p Paths, notLaunched error) []stdio.Tool {
|
||||
}, "required": []string{"label", "means"}}},
|
||||
"why": str("why the recommended option is the one you recommend"),
|
||||
"on_expiry": str("what you do if nobody answers before it expires, e.g. 'I leave the pull request open'"),
|
||||
"by": str("your session's name, as who@node or who, e.g. fix-589@g14"),
|
||||
"by": str("required: your session's name, as who@node or who, e.g. fix-589@g14"),
|
||||
"expires_minutes": map[string]any{"type": "integer",
|
||||
"description": "minutes until it expires, 5 to 1430; 60 when absent"},
|
||||
},
|
||||
@@ -191,7 +193,7 @@ func questionTools(p Paths, notLaunched error) []stdio.Tool {
|
||||
"with the option, who answered, on which channel and when; ended without a choice (expired, refused, " +
|
||||
"cancelled, replaced) with the router's words; or not proven, when the router's record does not hold to the " +
|
||||
"question asked. Read from the router's own record. wait_seconds (at most 60) waits for an answer within the " +
|
||||
"call: read at most once a minute.",
|
||||
"call; read a question at most once a minute (asked of you, not enforced).",
|
||||
Input: map[string]any{
|
||||
"key": str("the key claude_code_ask gave, e.g. q-0123456789abcdef"),
|
||||
"wait_seconds": map[string]any{"type": "integer", "description": "wait up to this many seconds (at most 60) while it is waiting"},
|
||||
|
||||
Reference in New Issue
Block a user