[Post-v0.1/P2] Require a point/stream class for every proposed verb #35
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#35
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up from #12 (P2).
Why
A verb whose output class depends on payload size gives consumers unstable framing and terminal rules. Future CLI/bridge proposals need to choose Point, Stream, or Exempt before implementation.
Scope
Acceptance criteria
Triage plan (2026-09-03)
Source-grounded triage against
mainat69076d42, reviewed through seven rounds of an independent reviewer (pisol/luna) until it passed with no findings. The dependency order that supersedes the tracker's is posted on #12.Claim check
Mostly already true at the reference and code level; the gap is the proposal-time rule and the contributor workflow.
docs/reference/cli.md:52-84defines Point/Stream/Exempt, lists every verb in exactly one row, and already states the extension rule: "Any machine-output verb added later lands with its classification in this table; a verb with no row here has no--formatflag." The stream terminal rule is atcli.md:557-575and its rationale atdocs/explanation/architecture/control-surfaces.md:236-300(why the class is explicit;--jsonrejected) and:310-330(one terminal object, clean vs failing are different objects).crates/felis-cli/src/cli_output.rs:68-91(PointFormat/StreamFormatclap shapes, the wrong framing refused before any dial),:253-370(Classenum withdebug_asserts so a point verb cannot emit items and a stream cannot emit a result).crates/felis-cli/src/tests.rs:261-300pins every verb's class.crates/felis-cli/src/cli_bridge.rs:387 fn is_streaming_op(op: &str)is a hard-coded list that must agree with the CLI table (cli.md:702-716documents the bridge's point/stream envelopes). Nothing ties the two together today..claude/skills/implement-feature/SKILL.md:20routes "New IPC message,felis sessionsverb, or CLI surface" toextend-ipc;extend-ipc/SKILL.md:92mentions the class only as an implementation step ("declare the class by flatteningPointFormatorStreamFormat"). The specific rule the issue wants — "maybe a stream if large" is always a Stream; every Stream names its terminal — appears nowhere (grep -rn "payload size\|if large" docs/ .claude/skills/is empty).Verdict
accept-with-changes: this is a docs-and-skill change of size S with no contract impact, so it does not need to wait for the tag — but it also does not need to block it. Two adjustments to the issue:
control-surfaces.md"Shared conventions", add the paragraph there and close this issue with it. Otherwise land it immediately post-tag.is_streaming_opand the CLI table are one source of truth (or a test asserts they agree). "Existing verbs remain fully classified" is otherwise a prose promise.Deferral cost with respect to the freeze: none. The classification itself freezes with #23/#29; this issue only writes down how future verbs join it, and that text can be added to an explanation doc any time without a contract change.
Approach
docs/explanation/architecture/control-surfaces.md"Shared conventions", the--formatbullet (:236): add a short "Proposing a verb" paragraph — the class is chosen at proposal time and is a property of the verb, never of the payload; a verb whose output might grow large is a Stream (the size-dependent verb is the rejected shape: it gives consumers unstable framing and terminal rules); each Stream proposal names its terminal count noun (count= sessions/rows/matches/notifications) and its failing terminal; Exempt is only for "nothing structured" or "a fixed framing of its own" (cli.md:69). Rejection criterion, in the doc's own voice: a proposal with no class, or a Stream with no terminal, is not reviewable. This is the decision record; no ADR layer (CLAUDE.md "Recording design decisions").docs/reference/cli.md:80-84: extend the existing "lands with its classification" sentence with "and, for a Stream, its terminal" so the reference twin states the fact..claude/skills/extend-ipc/SKILL.md: a step before wiring ("0. Classify: Point / Stream / Exempt, terminal named") and, in step 4, the bridge mirror (cli_bridge.rsis_streaming_op) besidePointFormat/StreamFormat.implement-feature/SKILL.md:20row text: "…(classify Point/Stream/Exempt first)".pub(crate) const STREAM_VERBStable incli_output.rsconsumed byis_streaming_opand by thetests.rs:261pin, or a test asserting the bridge list equals the Stream row. No wire, no schema, no CHANGELOG (contributor-facing only);skills/felisunchanged unless a verb changes.each_verb_accepts_only_its_own_classs_format(sic — typo in the test name attests.rs:264, fix while there) plus the bridge/CLI agreement test.Dependencies
cli_bridge.rsadmission and is the natural place to routeis_streaming_opthrough the shared table.Risk/effort
S. Risk is only doc drift between the three places (control-surfaces, cli.md, extend-ipc skill) — the
doc-cascadegrep sweep covers it.Labels
Keep
priority/P2. Consider a note that it may close as part of #23/#29's doc cascade rather than as a standalone change; if it does not ride those, it is a good first post-tagdocs:commit.