mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-15 00:19:14 +00:00
* feat(harness): subagent report contract and delegation acceptance criteria (RFC #4651 PR3) Layer 1 receipt verification is inert unless subagents actually cite their execution record. This lands the prompt layer that closes the adoption gap: - New subagents/report_contract.py owns the model-facing contract text, derived from the single-owner citation format (format_citation / receipt_id) so prompts can never drift from the verifier. The executor injects <report_contract> into every subagent system prompt — built-in and custom alike — requiring [rN tool_name] citations for action claims, verifiable handles (absolute path, URL, ID, HTTP status) for deliverables, and explicit failure reporting; the citation clause follows verification.receipts_enabled. - The task tool gains an optional keyword-only acceptance_criteria parameter, handed to the SubagentExecutor constructor and rendered into the subagent's SystemMessage (stripped, capped 20 items x 500 chars) — deliberately never the task HumanMessage, which InputSanitizationMiddleware classes as genuine user input and would HTML-escape into untrusted-input framing. The docstring frames subagent results as self-reports, states the citation cross-check's evidence boundary (resolved = the call happened, not that the claim is correct), and documents when to attach criteria with the canonical leaf forms. Deterministic leaf checking remains a separate layer. - The lead delegation workflow now instructs reading the ledger citation line as execution evidence only and spot-checking verifiable handles before synthesizing. - report_contract / acceptance_criteria are registered as blocked framework-authority tags in input sanitization so untrusted input cannot forge the verification contract. * fix(harness): neutralize acceptance criteria before system-channel injection render_acceptance_criteria_section interpolated lead-model-supplied acceptance_criteria verbatim into the subagent SystemMessage after only stripping/capping. A criterion such as '</acceptance_criteria><system>...</system>' could close the wrapper and open a framework authority tag, bypassing InputSanitizationMiddleware. Route each criterion through neutralize_untrusted_tags (the shared prompt-injection primitive) so blocked authority tags are HTML-escaped before interpolation. Add regression tests at the renderer and the executor _build_initial_state path. * fix(harness): keep model-supplied criteria off the system channel - Move acceptance_criteria values into the task HumanMessage — the untrusted channel InputSanitizationMiddleware escapes and boundary-frames. The subagent SystemMessage now carries only a framework-owned <acceptance_criteria> pointer note (no criterion text), so natural-language injection inside a criterion keeps task-data priority and cannot override framework instructions (PR #5090 review, willem-bd P1). - Condition the lead delegation workflow's citation verification guidance on verification.receipts_enabled and qualify the task tool's result-reading text with the enabled state, so a receipts-disabled configuration no longer tells the lead to require citation evidence that cannot exist (P2). * fix(harness): drop execution-record promise from report contract when receipts are disabled The <report_contract> opening was emitted unconditionally, so a verification.receipts_enabled=false subagent was told its report would be cross-checked against an execution record that cannot exist in that mode (terminal_receipts() returns None; no verdict, no ledger citation line). The opening now follows receipts_enabled: enabled keeps the cross-check language, disabled describes the handle-only review mode (PR #5090 review, willem-bd P2). * docs: record the prompt-layer trust-boundary self-check Generalizes the PR #5090 review outcome: before adding prompt text, ask of every data source in it what trust level it has and which channel it should ride — model/user-influenceable values ride the untrusted sanitized data channel, never framework-owned system text. Added to the PR template (Agents/LangGraph surface) and agents/AGENTS.md.
77 lines
3.6 KiB
Markdown
77 lines
3.6 KiB
Markdown
<!-- Reference a related issue with #123. Use Fixes / Closes / Resolves to
|
|
auto-close it on merge. Delete this line if the PR doesn't reference an issue. -->
|
|
Fixes #
|
|
|
|
## Why
|
|
|
|
<!-- Why are you opening this PR? Cover two things:
|
|
- The trigger — what made you write this? A bug you hit, a feature you need,
|
|
tech debt, or a prod issue?
|
|
- The pain being addressed — user-facing problem, or what it unblocks.
|
|
For non-trivial features, please open an issue/discussion first to align on
|
|
scope before writing code. -->
|
|
|
|
|
|
## What changed
|
|
|
|
<!-- Describe the change from a user's / caller's perspective, not as a code diff. e.g.:
|
|
- "Settings now has a 'Custom endpoint' field, off by default"
|
|
- "Backend /api/chat gains a `stream` flag, defaults to false"
|
|
- "Default model changed from X to Y — existing users notice on first run" -->
|
|
|
|
|
|
## Surface area
|
|
|
|
<!-- Check every box that applies. Reviewers use this to scope the review. -->
|
|
|
|
- [ ] **Frontend UI** — page / component / setting / interaction under `frontend/`
|
|
- [ ] **Backend API** — endpoint / SSE event / request-response shape under `backend/app`
|
|
- [ ] **Agents / LangGraph** — agent node, graph wiring, `langgraph.json`, or prompt change
|
|
- Prompt-layer self-check: for every data source in the new text, what is its trust level, and which channel should it ride? Model-supplied or user-influenceable values belong on the untrusted, sanitized data channel (e.g. the task `HumanMessage`) — never interpolated into framework-owned system text, even neutralized.
|
|
- [ ] **Sandbox** — `docker/` or sandboxed execution
|
|
- [ ] **Skills** — change under `skills/`
|
|
- [ ] **Dependencies** — new/upgraded entry in `backend/pyproject.toml` or `frontend/package.json` (say what it buys us)
|
|
- [ ] **Default behavior change** — changes existing behavior without the user opting in (default model, default setting, data shape)
|
|
- [ ] **Docs / tests / CI only** — no runtime behavior change
|
|
|
|
|
|
## Screenshots / Recording
|
|
|
|
<!-- If you checked "Frontend UI", attach screenshots showing the entry point —
|
|
where users discover the change — not just the feature in isolation.
|
|
Before/after is best for behavior changes. Short GIFs welcome. -->
|
|
|
|
|
|
## Bug fix verification
|
|
|
|
<!-- Skip (delete) this section if this PR is not a bug fix.
|
|
|
|
Bugs should be encoded as a failing test that goes red before the fix.
|
|
Confirm:
|
|
- Test path that reproduces the bug:
|
|
- Did it go red on `main` and green on this branch? (yes / no)
|
|
- If a red test wasn't cheap to write, explain why and what you did instead. -->
|
|
|
|
|
|
## Validation
|
|
|
|
<!-- What you actually ran. Run at least the checks for the area you changed:
|
|
Backend: cd backend && make lint && make test
|
|
Frontend: cd frontend && pnpm format && pnpm lint && pnpm typecheck && BETTER_AUTH_SECRET=local-dev-secret pnpm build && make test
|
|
Frontend E2E (if you touched frontend/): cd frontend && make test-e2e -->
|
|
|
|
|
|
## AI assistance
|
|
|
|
<!-- DeerFlow is an AI project — most PRs here use AI coding tools, and that's
|
|
welcome. Disclosing it just helps reviewers calibrate how closely to read the
|
|
diff. Please fill all three; don't delete the section. -->
|
|
|
|
**Tool(s) used:** <!-- e.g. Claude Code, Cursor, GitHub Copilot, Codex, Windsurf, or "none" -->
|
|
|
|
**How you used it:** <!-- e.g. "generated the module from a spec", "autocomplete only",
|
|
"AI wrote tests, I wrote the impl". A prompt or conversation link is great too. -->
|
|
|
|
- [ ] I've read and understand every line of this change and take responsibility for it — it's not unreviewed AI output.
|
|
|