Skip to content

feat(publish): let one piece claim every search it answered - #178

Draft
vraspar wants to merge 1 commit into
mainfrom
vraspar/publish-multi-searchid
Draft

feat(publish): let one piece claim every search it answered#178
vraspar wants to merge 1 commit into
mainfrom
vraspar/publish-multi-searchid

Conversation

@vraspar

@vraspar vraspar commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

One research thread fans out into many searchIds, and a piece answers the thread rather than one query of it. A live example from 2026-08-16: one investigation logged 10 MISS searchIds for what was a single question. The piece published back against one of them, and the other nine were closed one at a time as regenerated — false twice over, because they were not re-derived, they were answered by the same piece, and MISS-to-publish conversion counted every sibling of a converting loop as a failure.

tenjin publish --search-id now repeats, up to ten per piece.

What changed

  • --search-id collects (commander's collect, the same wiring feat(outcome): batch close, and a WebSearch hook that names itself #177 gave outcome). Ids dedupe, each is validated against SEARCH_ID_WIRE_RE exactly as today, and the cap refuses before any wallet touch.
  • Per-id local closes. closeNamedSearch runs once per id with unchanged relink semantics: a loop an outcome already closed is taken over, a loop an earlier publish claimed reports alreadyAnswered, and an id absent from the local store warns for itself rather than costing the others their close. Results land per id in the JSON envelope under searches, one human line each. A lone id keeps the flat search field callers have read since Publish-back loop DX: no defer status, invisible publish.mode, lost --search-id linkage (agent session trace) #161.
  • One prefill. The card's questionsAnswered is prefilled from the first named search this machine has a record of; the rest are attribution, not phrasing. Each receipt says which id supplied it.
  • Draft rule unchanged. A --draft answers nobody however many searches it names, so it sends no searchId at all and leaves every loop open.
  • MCP tenjin_publish takes searchId as a string or an array, matching what feat(outcome): batch close, and a WebSearch hook that names itself #177 did for the outcome tool.

Wire shape, and the deploy ordering

// one search — byte-identical to what ships today
{ "status": "published", "searchId": "0197…-…-eeee" }

// several — needs the sibling tenjin PR deployed
{ "status": "published", "searchId": ["0197…-…-aaaa", "0197…-…-bbbb"] }

A lone id stays the bare string, so a single-search publish keeps working against a post-create that only takes a scalar. The array form requires the sibling tenjin PR (branch vraspar/post-lookups-multi-searchid) deployed first — an array against the current validator is a hard 400. The changeset states the same ordering.

The committed OpenAPI fixture still declares the scalar, so contract.test.ts continues to pin the single-id body against it; the array is pinned at the builder in posts-api.test.ts instead.

Rejected alternatives, honored

Per #167, none of these are here and none should arrive later: no similarity auto-linking (false links poison outcome data), no close-all-session-MISSes on publish (one session holds unrelated investigations), no explicit thread handles on searches.

Scope

The outcome half of #167 shipped separately in #177 (tenjin outcome --search-id repeating, plus --all-open). This PR is the publish half and closes the issue. Nothing here touches ephemeral status or the auto-sweep, which belong to the other train.

Design record: tenjin-notes/plans/2026-08-17-outcome-taxonomy-and-167.md, "Closing #167".

Closes #167.

Tests

New coverage in src/commands/publish.test.ts, src/lib/posts-api.test.ts, and src/cli.test.ts: the dispatcher collects rather than replacing (read back through the cap, which only eleven surviving ids can trip); the wire carries an array for several ids and a bare string for one; repeats collapse on the wire and in the ledger; the cap refuses before the keystore opens; a mix of closeable, relinked, already-answered, and absent ids each reports honestly; the prefill names its source; a multi-id draft sends nothing.

pnpm typecheck, pnpm lint, and prettier --check are clean. Full vitest run: 2148 passed, 2 failed — both pre-existing in doctor.test.ts on this machine and reproduced identically at origin/main (they read the developer's real installed skills state).

🤖 Generated with Claude Code

`tenjin publish --search-id` repeats, up to ten per piece. A research
thread fans out into many searchIds, and until now the piece published
back against one while the siblings were closed as `regenerated` — false
twice over, and it counted every sibling of a converting loop as a MISS.

Each id closes its own local loop with publish attribution (relinking
one an `outcome` already closed, exactly as a single id does today), and
the JSON envelope reports per id under `searches`, so an id this machine
never recorded warns for itself instead of costing the others their
close. A lone id keeps the flat `search` field.

On the wire, one id stays the bare string it has always been and several
become an array, so a single-search publish is byte-identical against a
server that predates the array.

Closes #167.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A1igator added a commit that referenced this pull request Aug 18, 2026
The live hosted skill moved again tonight (the #178 multi-searchId wire
form), so the drift gate reddened on this branch, which merged main
before the move. Mirror-only: scripts/sync-skill.mjs writes
skills/tenjin/SKILL.md and nothing else, so the scan rule data, the
corpus, and the tenjin-publish detector lists are untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

publish and outcome should accept multiple --search-id values

1 participant