Skip to content

feat(server): add PUT /api/knowledge/{scenario} write endpoint#1059

Merged
jayscambler merged 2 commits into
mainfrom
feat/knowledge-put-endpoint
Jun 9, 2026
Merged

feat(server): add PUT /api/knowledge/{scenario} write endpoint#1059
jayscambler merged 2 commits into
mainfrom
feat/knowledge-put-endpoint

Conversation

@jayscambler

Copy link
Copy Markdown
Contributor

Summary

Companion to the read endpoint merged in #1056. The cowork GUI's knowledge editor ("Save changes") and document-to-knowledge flow need to write curated knowledge back to the engine.

PUT /api/knowledge/{scenario} overwrites a scenario's playbook.md / hints.md / dead_ends.md from a {playbook, hints, deadEnds} JSON payload:

  • Writes only the fields provided as strings (partial updates are fine; non-string values are ignored).
  • Returns {scenario, written: [...]} listing which files were written.
  • Same scenario-id validation (length, alphanumeric with _/-) and resolve-under-knowledge_root path guard as the GET.

Tests

tests/test_knowledge_endpoint.py (the GET shipped untested in #1056; these exercise both directions):

  • PUT writes all three files and the GET round-trips them.
  • PUT with a partial payload writes only the provided string fields.
  • Invalid scenario ids (bad!name, dots..dots, 129 chars) are rejected with 400.

Checks in a clean worktree off main: new tests + test_server_health.py pass, ruff clean, mypy clean.

Companion to the read endpoint from #1056. The cowork GUI's knowledge
editor and document-to-knowledge flow need to write curated knowledge
back: this overwrites a scenario's playbook.md / hints.md / dead_ends.md
from a {playbook, hints, deadEnds} JSON payload, writing only the fields
provided as strings and reporting which files were written.

Same scenario-id validation and resolve-under-knowledge-root path guard
as the GET. Tests cover the write + GET round-trip, partial payloads,
and invalid-id rejection (the read endpoint shipped untested in #1056;
these tests exercise both directions).
ArtifactStore.read_hints prefers structured hint_state.json over flat
hints.md, so a GUI hints edit that only wrote hints.md was silently
ignored by the next run/export/MCP read (reviewer P2).

Drop hint_state.json when the PUT accepts hints: the operator's flat
edit becomes authoritative for both read paths. read_hints returns it
verbatim, and read_hint_manager lazily rebuilds structured state from
it via the existing legacy fallback. Clearing beats rewriting the
structured state through HintManager.from_hint_text, whose line-based
parse and max-hints cap would mangle arbitrary GUI markdown.

A PUT without hints leaves hint_state.json untouched. Regression test
reproduces the reviewer's seeded hint_state scenario and was verified
to fail without the fix.
@jayscambler

Copy link
Copy Markdown
Contributor Author

Addressed the P2 in 8ec4b39: when the PUT accepts hints, it now deletes hint_state.json so the flat edit is authoritative for both read paths (read_hints returns it verbatim; read_hint_manager lazily rebuilds structured state via the existing legacy fallback).

Chose clearing over rewriting structured state through HintManager.from_hint_text: its line-based parse and max-hints cap would mangle arbitrary GUI markdown (e.g. document text appended to hints). A PUT without hints leaves hint_state.json untouched.

Added a regression test reproducing the seeded hint_state.json repro; verified it fails without the fix. ruff + mypy clean.

@jayscambler jayscambler merged commit aa8b707 into main Jun 9, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant