Skip to content

fix(proposals): knowledge_record reports success when a proposal parses to zero FILE blocks #207

Description

@drewstone

Summary

knowledge_record (and the applyKnowledgeWriteBlocks it wraps) returns success with written: [] and warnings: [] when the supplied proposal contains no parseable ---FILE: <path>--- block. The caller cannot distinguish "wrote nothing because you asked for nothing" from "your write was silently dropped".

An agent that records a page in a plausible but wrong block format gets an OK, believes the page landed, and proceeds. Nothing is written, nothing warns, and the loss is only discovered later by a reader that finds the page missing.

Where

src/proposals.tsapplyKnowledgeWriteBlocks, and its parser parseKnowledgeWriteBlocks.

In the parser's scan loop, a line that does not match OPENER_LINE is skipped with no record:

const opener = OPENER_LINE.exec(lines[i])
if (!opener) { i++; continue }

so a proposal with zero openers yields blocks: [] and warnings: []. applyKnowledgeWriteBlocks then takes the parsed.blocks.length > 0 branch not at all and returns { written: [], warnings: [] }.

Malformed-but-closed and unsafe-path blocks do warn. Only "no opener matched anywhere" is silent, which is the most likely agent error.

Reproduction

Against @tangle-network/agent-knowledge@15.0.2:

import { applyKnowledgeWriteBlocks, initKnowledgeBase } from '@tangle-network/agent-knowledge'
const root = mkdtempSync(tmpdir() + '/kb-')
await initKnowledgeBase(root, {})

// A: no FILE block at all
await applyKnowledgeWriteBlocks(root, '# director-a proposal\n\nSome content.\n', { pagesDirectory: 'pages' })
// B: near-miss opener (spaces inside the delimiters)
await applyKnowledgeWriteBlocks(root, '--- FILE pages/p.md ---\nbody\n--- END FILE ---\n', { pagesDirectory: 'pages' })
// C: correct
await applyKnowledgeWriteBlocks(root, '---FILE: pages/p.md---\nbody\n---END FILE---\n', { pagesDirectory: 'pages' })

Observed:

A: no FILE block at all (plain markdown)
   -> RETURNED OK  written=[] warnings=[]
B: wrong opener fence style
   -> RETURNED OK  written=[] warnings=[]
C: correct opener/closer
   -> RETURNED OK  written=["pages/p.md"] warnings=[]

A and B are indistinguishable from a legitimate no-op at the call site.

Observed in a real run

Run collab-decoder-20260910-r2 (two directors sharing one Knowledge namespace, retriever 15.0.2). director-a recorded its problem-choice page and wrote in its own journal that it had posted Knowledge page director-a/proposal-v1. The page never existed: the root's knowledge_read returned missing and three knowledge_search visibility snapshots taken at 23:40:53Z, 23:41:29Z and 23:48:03Z all record pageCount: 0 under snapshot digest sha256:ae865c5e53953aadf6f850baef49f2cdd0c68f23f99b98a3fd97207b413f48bc.

The peer director-b recovered independently by writing a throwaway probe-b.md page to discover the accepted format, then used its own Knowledge page to tell director-a what the working format was — a workaround for a silent failure that should have been an error at the call.

No information was lost in that run only because the directors had chosen the filesystem as their medium of record and Knowledge as a mirror. A run that trusted Knowledge would have lost the page with no signal.

Requested behavior

Make a dropped write observable at the call:

  1. When proposalText is non-empty (ignoring whitespace) and the parser produced zero blocks, push a warning naming the expected opener/closer form — e.g. no FILE block found; expected an opener line "---FILE: <pagesDirectory>/<path>---" and closer "---END FILE---". This is the minimum fix and is backward compatible.
  2. Preferably, also have knowledge_record reject the call (a ValidationError) on non-empty input that yields zero blocks, so an agent gets a corrective error rather than a success it has to verify by reading back.

A test asserting that a non-empty proposal with no recognizable block either warns or throws would cover both.

Impact

Silent data loss in the Knowledge layer for any agent that mis-formats a write. The agent's own account of what it recorded diverges from the store, which corrupts downstream lineage and any claim that cites the page.

Environment

  • @tangle-network/agent-knowledge 15.0.2
  • @tangle-network/agent-runtime 0.208.1
  • node v22.23.2, darwin 25.1.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions