mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(tui): a runtime continuation is not the person's message
The terminal counted and drew a host-authored inbox entry as the person's own pending line, so `d d` on it reached DELETE /inbox/items/<id> and the kernel dropped the runtime's continuation. The desktop has always filtered these out; the terminal never decoded the field it filters on. The mirror now carries origin, readonly and capacity, and a declared-subset wire-parity check holds it (and the page's copy) to the fields a decision reads, so dropping one is a `make check` failure instead of a silent zero. A host continuation is out of the pending rows and out of the count, except where it is blocked or uncertain and needs a decision only the person can make; there it is drawn marked as the runtime's. A read-only snapshot offers no mutation key and says why.
This commit is contained in:
1 parent
add2e45c05
commit
216ea245fd
8 files changed
+660
-36
No files matched your search
@@ -38,6 +38,10 @@ const (
|
||||
tsSurfaceFile = "desktop/frontend-next/src/ui/surface/spec.ts"
|
||||
)
|
||||
|
||||
// tuiInboxFile is where the terminal re-declares the kernel's inbox wire types
|
||||
// by hand. One file holds both the listing and its entries.
|
||||
const tuiInboxFile = "internal/frontend/tui/client_inbox.go"
|
||||
|
||||
// mirroredWireTypes are the Go types the desktop keeps a second, hand-written
|
||||
// copy of: the graph, whose consumers all fold the same deltas, and the sandbox
|
||||
// editor, which draws a security posture a field it cannot read would get
|
||||
@@ -324,6 +328,40 @@ var mirroredWireTypes = []wireMirror{
|
||||
{"internal/frontend/serve/appearance.go", "wallpaperView", tsLookFile, "Wallpaper"},
|
||||
}
|
||||
|
||||
// partialWireMirrors are hand-written copies honestly smaller than the kernel's
|
||||
// struct: a terminal draws one row per entry and the page's outbox shows the
|
||||
// person's lines. Each pair names the fields it must carry and is compared both
|
||||
// ways over those only; a subset's failure mode is a field quietly dropped, and
|
||||
// the full-mirror list above is not relaxed for it.
|
||||
var partialWireMirrors = []partialWireMirror{
|
||||
// What a surface reads to decide whose a queued line is, whether it may act
|
||||
// on it, and whether the queue can be changed at all.
|
||||
{"internal/state/sessioninbox/types.go", "InboxItemMeta", tuiInboxFile, "InboxItem",
|
||||
[]string{"id", "intent", "state", "preview", "origin", "images", "blockCode", "blockReason"}},
|
||||
{"internal/state/sessioninbox/types.go", "InboxSnapshot", tuiInboxFile, "InboxSnapshot",
|
||||
[]string{"revision", "paused", "readonly", "recovered", "recoveredCount", "unrecovered", "items", "capacity"}},
|
||||
{"internal/state/sessioninbox/types.go", "Capacity", tuiInboxFile, "InboxCapacity",
|
||||
[]string{"items", "maxItems", "bytes", "maxBytes", "maxItemBytes"}},
|
||||
// The page reads the same two types through its own hand-written copy. It
|
||||
// omits the manifest fields neither surface draws, and must keep the ones
|
||||
// its outbox decides from.
|
||||
{"internal/state/sessioninbox/types.go", "InboxItemMeta", tsPortFile, "QueueItem",
|
||||
[]string{"id", "intent", "origin", "state", "preview", "createdAt", "blockReason", "blockCode", "refs", "images"}},
|
||||
{"internal/state/sessioninbox/types.go", "InboxSnapshot", tsPortFile, "Queue",
|
||||
[]string{"revision", "paused", "readonly", "recoveredCount", "items", "capacity"}},
|
||||
{"internal/state/sessioninbox/types.go", "Capacity", tsPortFile, "QueueCapacity",
|
||||
[]string{"items", "maxItems", "bytes", "maxBytes"}},
|
||||
}
|
||||
|
||||
// partialWireMirror is one declared subset: readerFile's readerType must carry
|
||||
// every name in fields, the writer must still send each, and the reader must
|
||||
// choose no name the writer never sends. What is left out is the point.
|
||||
type partialWireMirror struct {
|
||||
writerFile, writerType string
|
||||
readerFile, readerType string
|
||||
fields []string
|
||||
}
|
||||
|
||||
type wireMirror struct {
|
||||
goFile, goType, tsFile, tsType string
|
||||
}
|
||||
@@ -369,6 +407,16 @@ func (w *wireScan) observe(src *sourceFile) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, m := range partialWireMirrors {
|
||||
for _, side := range [][2]string{{m.writerFile, m.writerType}, {m.readerFile, m.readerType}} {
|
||||
if side[0] != src.rel {
|
||||
continue
|
||||
}
|
||||
if names, ok := wireFieldNames(src.file, side[1]); ok {
|
||||
w.fields[side[0]+"."+side[1]] = names
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findings compares each declared pair both ways. Either direction is a defect:
|
||||
@@ -386,7 +434,21 @@ func (w *wireScan) findings(root string) []Finding {
|
||||
}
|
||||
bodies[m.tsFile] = string(raw)
|
||||
}
|
||||
for _, m := range partialWireMirrors {
|
||||
if !strings.HasSuffix(m.readerFile, ".ts") {
|
||||
continue
|
||||
}
|
||||
if _, read := bodies[m.readerFile]; read {
|
||||
continue
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(root, m.readerFile))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
bodies[m.readerFile] = string(raw)
|
||||
}
|
||||
out := append(wireParityFindings(mirroredWireTypes, w.fields, bodies), readParityFindings(readMirrors, w.fields)...)
|
||||
out = append(out, partialWireFindings(partialWireMirrors, w.fields, bodies)...)
|
||||
return append(out, wireConstantFindings(root, mirroredWireConstants)...)
|
||||
}
|
||||
|
||||
@@ -416,6 +478,57 @@ func readParityFindings(mirrors []readMirror, goFields map[string][]string) []Fi
|
||||
return out
|
||||
}
|
||||
|
||||
// partialWireFindings checks a declared subset in the three directions that
|
||||
// matter: the writer must still send each named field, the reader must still
|
||||
// carry it, and the reader may not read a name nothing sends. What the writer
|
||||
// sends and a subset ignores is the reason a subset exists.
|
||||
func partialWireFindings(mirrors []partialWireMirror, goFields map[string][]string, bodies map[string]string) []Finding {
|
||||
var out []Finding
|
||||
for _, m := range mirrors {
|
||||
writer, sends := goFields[m.writerFile+"."+m.writerType]
|
||||
if !sends {
|
||||
out = append(out, Finding{m.writerFile, 1, ruleWireParity,
|
||||
fmt.Sprintf("%s is declared a mirrored wire type and is not a struct here", m.writerType), 1})
|
||||
continue
|
||||
}
|
||||
reader, line, declares := partialReaderFields(m, goFields, bodies)
|
||||
if !declares {
|
||||
out = append(out, Finding{m.readerFile, 1, ruleWireParity,
|
||||
fmt.Sprintf("%s is declared a partial mirror of %s.%s and is not declared here", m.readerType, m.writerFile, m.writerType), 1})
|
||||
continue
|
||||
}
|
||||
for _, name := range m.fields {
|
||||
if !slices.Contains(writer, name) {
|
||||
out = append(out, Finding{m.writerFile, 1, ruleWireParity,
|
||||
fmt.Sprintf("%s no longer sends %q, a field %s decides from", m.writerType, name, m.readerType), 1})
|
||||
}
|
||||
if !slices.Contains(reader, name) {
|
||||
out = append(out, Finding{m.readerFile, line, ruleWireParity,
|
||||
fmt.Sprintf("%s no longer declares %q, which %s.%s sends and this copy decides from", m.readerType, name, m.writerFile, m.writerType), 1})
|
||||
}
|
||||
}
|
||||
for _, name := range missingFrom(reader, writer) {
|
||||
if slices.Contains(m.fields, name) {
|
||||
// Already named as the writer dropping a declared field.
|
||||
continue
|
||||
}
|
||||
out = append(out, Finding{m.readerFile, line, ruleWireParity,
|
||||
fmt.Sprintf("%s reads %q and %s.%s never sends it", m.readerType, name, m.writerFile, m.writerType), 1})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// partialReaderFields reads one side of a pair from whichever grammar declares
|
||||
// it: the terminal's copy is Go, the page's is TypeScript.
|
||||
func partialReaderFields(m partialWireMirror, goFields map[string][]string, bodies map[string]string) ([]string, int, bool) {
|
||||
if strings.HasSuffix(m.readerFile, ".ts") {
|
||||
return tsInterfaceFields(bodies[m.readerFile], m.readerType)
|
||||
}
|
||||
names, ok := goFields[m.readerFile+"."+m.readerType]
|
||||
return names, 1, ok
|
||||
}
|
||||
|
||||
// wireParityFindings is the comparison itself, so a fixture can carry the one
|
||||
// pair a case is about rather than the tree's real ones.
|
||||
func wireParityFindings(mirrors []wireMirror, goFields map[string][]string, bodies map[string]string) []Finding {
|
||||
|
||||
@@ -190,3 +190,107 @@ func TestWireParitySkipsAnEmbeddedTypeFromAnotherPackage(t *testing.T) {
|
||||
t.Fatalf("names = %v ok=%v, want only the struct's own field", names, ok)
|
||||
}
|
||||
}
|
||||
|
||||
const partialWriter = `package p
|
||||
|
||||
type Item struct {
|
||||
ID string ` + "`json:\"id\"`" + `
|
||||
Origin string ` + "`json:\"origin,omitempty\"`" + `
|
||||
Preview string ` + "`json:\"preview\"`" + `
|
||||
}
|
||||
`
|
||||
|
||||
const partialReader = `package p
|
||||
|
||||
type ItemView struct {
|
||||
ID string ` + "`json:\"id\"`" + `
|
||||
Origin string ` + "`json:\"origin\"`" + `
|
||||
Preview string ` + "`json:\"preview\"`" + `
|
||||
}
|
||||
`
|
||||
|
||||
var partialPair = []partialWireMirror{
|
||||
{"w.go", "Item", "r.go", "ItemView", []string{"id", "origin", "preview"}},
|
||||
}
|
||||
|
||||
func partialFindingsFor(t *testing.T, writer, reader string) []Finding {
|
||||
t.Helper()
|
||||
w, r := parseBytes("w.go", []byte(writer)), parseBytes("r.go", []byte(reader))
|
||||
wn, ok := wireFieldNames(w.file, "Item")
|
||||
if !ok {
|
||||
t.Fatal("the fixture's writer does not resolve")
|
||||
}
|
||||
rn, ok := wireFieldNames(r.file, "ItemView")
|
||||
if !ok {
|
||||
t.Fatal("the fixture's reader does not resolve")
|
||||
}
|
||||
return partialWireFindings(partialPair, map[string][]string{
|
||||
"w.go.Item": wn,
|
||||
"r.go.ItemView": rn,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// A subset is allowed to ignore what the writer sends; the names it declares
|
||||
// are the whole contract.
|
||||
func TestPartialWireParityAcceptsACopyThatCarriesItsDeclaredFields(t *testing.T) {
|
||||
if got := partialFindingsFor(t, partialWriter, partialReader); len(got) != 0 {
|
||||
t.Fatalf("a faithful subset must produce no findings, got %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// The defect the sessioninbox guard exists for: the copy drops a field a
|
||||
// decision reads, and the empty value looks like a real answer.
|
||||
func TestPartialWireParityCatchesAFieldTheCopyDropped(t *testing.T) {
|
||||
reader := strings.Replace(partialReader, "\tOrigin string `json:\"origin\"`\n", "", 1)
|
||||
got := partialFindingsFor(t, partialWriter, reader)
|
||||
if len(got) != 1 || !strings.Contains(got[0].Msg, `no longer declares "origin"`) {
|
||||
t.Fatalf("a dropped decision field was not reported: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// A rename on the reading side reads as zero as surely as a dropped field.
|
||||
func TestPartialWireParityCatchesANameTheCopyInvented(t *testing.T) {
|
||||
reader := strings.Replace(partialReader, `json:"origin"`, `json:"orgin"`, 1)
|
||||
got := partialFindingsFor(t, partialWriter, reader)
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("findings = %+v, want the dropped name and the invented one", got)
|
||||
}
|
||||
var sawDropped, sawInvented bool
|
||||
for _, f := range got {
|
||||
sawDropped = sawDropped || strings.Contains(f.Msg, `no longer declares "origin"`)
|
||||
sawInvented = sawInvented || strings.Contains(f.Msg, `reads "orgin"`)
|
||||
}
|
||||
if !sawDropped || !sawInvented {
|
||||
t.Fatalf("findings = %+v, want both directions named", got)
|
||||
}
|
||||
}
|
||||
|
||||
// The writer's side is checked too: a field the kernel stops sending is a
|
||||
// decision the copy can no longer make.
|
||||
func TestPartialWireParityCatchesAFieldTheWriterStoppedSending(t *testing.T) {
|
||||
writer := strings.Replace(partialWriter, "\tOrigin string `json:\"origin,omitempty\"`\n", "", 1)
|
||||
got := partialFindingsFor(t, writer, partialReader)
|
||||
if len(got) != 1 || !strings.Contains(got[0].Msg, `no longer sends "origin"`) {
|
||||
t.Fatalf("a field the writer stopped sending was not reported: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// The page's copy is TypeScript and read from its interface, not its wording.
|
||||
func TestPartialWireParityReadsThePagesInterface(t *testing.T) {
|
||||
const ts = "export interface ItemView {\n id: string;\n origin?: \"host\";\n preview: string;\n}\n"
|
||||
mirrors := []partialWireMirror{{"w.go", "Item", tsWireFile, "ItemView", []string{"id", "origin", "preview"}}}
|
||||
names, ok := wireFieldNames(parseBytes("w.go", []byte(partialWriter)).file, "Item")
|
||||
if !ok {
|
||||
t.Fatal("the fixture's writer does not resolve")
|
||||
}
|
||||
if got := partialWireFindings(mirrors, map[string][]string{"w.go.Item": names},
|
||||
map[string]string{tsWireFile: ts}); len(got) != 0 {
|
||||
t.Fatalf("a faithful TS subset must produce no findings, got %+v", got)
|
||||
}
|
||||
dropped := strings.Replace(ts, " origin?: \"host\";\n", "", 1)
|
||||
got := partialWireFindings(mirrors, map[string][]string{"w.go.Item": names},
|
||||
map[string]string{tsWireFile: dropped})
|
||||
if len(got) != 1 || !strings.Contains(got[0].Msg, `no longer declares "origin"`) {
|
||||
t.Fatalf("a dropped field in the page's copy was not reported: %+v", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user