A fill while the agent restarts is judged by the last view held and, refused, is owed and told again (issue 924)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 23:03:58 +01:00
parent 7add29cffe
commit c55672ce5e
4 changed files with 101 additions and 4 deletions
+14 -1
View File
@@ -100,6 +100,13 @@ type owed struct {
unnamed map[string]bool // refused: the declaration did not name it as filled
failed map[string]bool // attempted, and the write or what reads it failed
noAccount bool // refused: the declaration named no provisioner account
unread bool // refused: the declaration could not be read (a restart of the agent, a bus that is gone)
}
func (o *owed) setUnread() {
o.mu.Lock()
o.unread = true
o.mu.Unlock()
}
func (o *owed) add(kind *map[string]bool, path string) {
@@ -115,6 +122,7 @@ func (o *owed) add(kind *map[string]bool, path string) {
func (e *Engine) Accepts(uid uint32) error {
d, err := e.Declared()
if err != nil {
e.owed.setUnread()
return fmt.Errorf("this node's declaration cannot be read, so no write is accepted: %w", err)
}
if err := JudgeAccount(d, uid, e.UIDOf); err != nil {
@@ -147,6 +155,7 @@ func (e *Engine) Write(ctx context.Context, uid uint32, req Request) Answer {
defer unlock()
d, err := e.Declared()
if err != nil {
e.owed.setUnread()
return Refusal(req.Path, "this node's declaration cannot be read, so nothing was written: "+err.Error())
}
if err := JudgeAccount(d, uid, e.UIDOf); err != nil {
@@ -184,7 +193,7 @@ func (e *Engine) Knock(socket string) (string, error) {
}
defer unlock()
e.owed.mu.Lock()
nothing := len(e.owed.unnamed) == 0 && len(e.owed.failed) == 0 && !e.owed.noAccount
nothing := len(e.owed.unnamed) == 0 && len(e.owed.failed) == 0 && !e.owed.noAccount && !e.owed.unread
e.owed.mu.Unlock()
if nothing {
return "", nil
@@ -206,6 +215,10 @@ func (e *Engine) Knock(socket string) (string, error) {
why = append(why, p+" (its write failed)")
delete(e.owed.failed, p)
}
if e.owed.unread {
why = append(why, "every file (the declaration can be read now)")
e.owed.unread = false
}
if e.owed.noAccount && d.Provisioner != "" {
why = append(why, "every file (the provisioner's account is named now)")
e.owed.noAccount = false
+8
View File
@@ -118,7 +118,12 @@ func TestAnUndeclaredPathIsRefused(t *testing.T) {
after string // the declaration applied afterwards
knock string // what the knock names; "" for none
twice bool // a second knock names nothing
// the declaration could not be read when the write came (the agent restarts): owed, and told once it can be
unreadable bool
}{
{name: "a write refused while the declaration could not be read", before: withAccount, unreadable: true,
account: provisionerUID, path: "/etc/m/m.conf", after: withAccount,
knock: "every file (the declaration can be read now)", twice: true},
{name: "an undeclared path the next declaration does not name either", before: withAccount,
account: provisionerUID, path: "/etc/m/other.conf", after: withAccount},
{name: "an undeclared path the next declaration names", before: withAccount, account: provisionerUID,
@@ -141,6 +146,9 @@ func TestAnUndeclaredPathIsRefused(t *testing.T) {
return "", "", row.place
}
}
if row.unreadable {
e.Declared = func() (*declaration.Declaration, error) { return nil, errors.New("the agent restarts") }
}
_ = e.Accepts(row.account)
e.Write(context.Background(), row.account, Request{Path: row.path, Content: []byte("x")})
after := declared(t, row.after)