Repository navigation
Let a native chat pin a skill and a node, and open a play's Edit chat bound to it - #3575
Conversation
Pragmatic Code Review: PR #3575 (issue #3520)Review Type: Initial Review Reviewed at SummaryThe change is a clear net improvement and follows the design in ADR-090 §3 to §5 closely. The layering is right: one declared relationship, pins read from edges each turn into an ephemeral session (so nothing goes stale), one pure routing function ( Requirements Check
Code Review Findings🟡 Important1. A pin clears the destructive bar as well as the read and mutating ones
Failure scenario: a chat pins a skill that whitelists Recommendation: let a pin stand in for the read and mutating bars only, for example Principle: least privilege; keep an existing safety invariant unless the feature needs it relaxed (YAGNI applies to the relaxation). 2. An assistant-first conversation is a new shape for the chat template, and no test renders it The first turn of an Edit chat sends I could not check this by reading: it depends on the locked model's template. If it fails, the first turn of every Edit chat fails with a template error, which is the feature's main path. Recommendation: one live turn in an Edit chat on the locked native model before merge (the Principle: test at the boundary where the risk is; a stub at the seam verifies the caller's intent, not the callee's contract. 3. Nothing tests the seam between a chat's pins and routing Each half is tested: Recommendation: one daemon test that seeds the skill, creates the Edit chat, runs a turn on the recording engine and asserts the request's system prompt contains the skill's instructions (or that the offered tools include Principle: integration tests belong on critical paths where two separately tested units meet. 🟢 Suggestions4. A Stage-1 failure drops the pinned skill, while "routing unavailable" keeps it When the Stage-1 generation errors, 5. The compensating delete has no test
6. A 7. The warning for a skipped pin assumes which one was skipped
8. A long ReAct loop can summarize the pinned record away mid-turn
Nitpicks
What is done well
RecommendationAPPROVE, with findings 1 to 3 recommended before merge: 1 and 3 are a few lines each, and 2 is one live turn. Criterion 7d (the evals) needs either a run or the author's explicit acceptance of the unchanged-path argument. |
malibio
left a comment
There was a problem hiding this comment.
Automated pragmatic-code-review: APPROVE (posted as a comment — GitHub blocks self-approval). Full findings in the review comment above.
Address reviewChanges are in Addressed
Found by the full test run, not the review
Skipped
Re-Review DecisionDecision: RE-REVIEW NEEDED Rationale: the fixes change the routing gate, add a second degraded-path branch, and change which relationship groups core lists for every node, which the first review did not see. |
Pragmatic Code Review: PR #3575 (issue #3520)Review Type: Re-Review Covers Previous Review Summary
Previous findings
New Findings1. 🟡 Important: the any-target rule changes every untyped relationship, and only The skip is keyed on
Both are defensible (an Add entry and a form field on every node in the graph for every untyped relationship is the same noise
Recommendation: correct the doc comment (typed groups are listed when empty; an untyped one only on a node it reaches, with the reason), add a few lines to the new test or a sibling for a user schema's untyped relationship (empty on an unlinked node, present on a linked one), and add one sentence to Principle: a comment that contradicts the code is worse than none; a general rule needs a test at its general case, not only at the case that prompted it. 2. 🟢 Suggestion: a pinned destructive skill now logs "the bar is set too high" on every turn
3. 🟢 Suggestion: the destructive exception is all-or-nothing for the skill, and nothing guards Play Authoring against it A pinned skill whose whitelist includes one data-removing tool loses the pin's effect entirely below the destructive bar: its instructions, its schemas and its read and write tools, not only the destructive tool. That is the safe direction and the right default. The consequence is that adding 4. 🟢 Suggestion: the Stage-1 failure branch has no test The unavailable path has Checked and found sound
RecommendationAPPROVE All three Important findings from the first review are resolved in code, the skips are reasonable, and the new commit introduces no correctness or security problem. New finding 1 is worth doing before merge: it is a comment correction, a short test and one docs sentence, for a behaviour change that reaches user schemas. Findings 2 to 4 are optional. |
malibio
left a comment
There was a problem hiding this comment.
Automated pragmatic-code-review (re-review): APPROVE (posted as a comment — GitHub blocks self-approval). Full findings in the review comment above.
Address re-reviewChanges are in Addressed
Skipped
Re-Review DecisionDecision: NO RE-REVIEW NEEDED Rationale: the one code change narrows a rule back toward the behaviour before this PR, in the direction the re-review asked about, with a test on each side of it. The rest is a test assertion and comments. |
…and entity list (#3520) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r's Edit button (#3520) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pins-to-routing seam, list pinned_in only where a chat pins (#3520) - A pin no longer clears the destructive score bar - A Stage-1 failure keeps the pinned skill, as an unavailable route does - Daemon test: a chat's pinned skill reaches the model - Live test: the E4B template renders an assistant-first conversation - An any-target relationship's reverse is listed only on nodes it reaches, so pinned_in is not an empty group on every node - A blank provider or model is none; the skipped-pin warning names the pin Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…at's (#3520) The empty-group rule is now keyed on the declaring type being a chat, so a user schema's untyped relationship is still listed on a node with no edge. Guard that Play Authoring stays non-destructive, which its pin depends on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ee0c81a to
62a6c52
Compare
Closes #3520. Implements ADR-090 §3–§5.
What changed
Node type change (node type sequence): a new declared relationship on the core subtype
ai-chat-native(anai-chatsubtype).pins(chat → any node, many; reversepinned_in, many; no edge fields). No fields added or changed, no structural or participation rule changed. Generated TypeScript is unchanged. The sheet innode-types.mdalready lists it.Routing (agent)
SkillCandidate.pinned; a pinned candidate clears the score gate without a score.routing::with_pinned_skillsadds each pinned skill to Stage 2's candidates after the top-K bound, so it takes no retrieved skill's place. A skill retrieval also selected is marked, not repeated, and keeps its score.Entity list (daemon)
chat_pins::load_chat_pinsreads a chat's pins each turn and splits them into skills (routing candidates, viaskill_ops::skills_by_id) and other nodes.Edit chat
LocalAgentService.CreatePlayEditChat(Tauri commandlocal_agent_create_play_edit_chat, dev-proxyPOST /api/agent/play-edit-chat). It creates the chat titledEdit <play title>, its pins to the play-authoring skill and the play, and the opening assistant message (outcome: clarified, five options, a link to the play). No model call.PlayNodeViewergets an Edit button that creates the chat and opens it in the other pane, beside the play.optionsrendering; picking one sends it as the user's turn.Decisions worth a look
clarified(so its options render), it otherwise tripped the one-clarification-per-request contract: after the user's first pick the agent was re-prompted "do not ask anything further".prior_turns_from_historynow leaves out assistant messages that precede the first user message.applies_tolink carries no schema metadata:find_skills' fallback is a guess from the query, and a pin has none.Tests
pins.with_pinned_skills(added, not repeated, keeps retrieval's score, no-op without pins), gate, offered set; turn-level Stage 2 prompt and tools with and without pins, with retrieval finding the pinned skill, finding nothing, and unavailable; the seeded Play Authoring skill as a pinned candidate.mentionsedge, model, missing skill, non-play); first turn answers the reply to the opening message; pinned node present past the cap; no record for a chat without pins.createPlayEditChat, the Edit button, the opening card's options.The assembled prompt is unchanged for a chat with no pins, so the prompt golden test is untouched. The routing and decisions evals were not re-run: they exercise chats without pins, whose routing path is covered as unchanged by unit tests.
Docs: two sentences added to
architecture/ai-chat-node.mdandarchitecture/prompt-skill-system.mdin nodespace-docs.🤖 Generated with Claude Code