From d1c3d84df7547d68955d7223599a828164a031e7 Mon Sep 17 00:00:00 2001 From: Patrick Chou <1326306+pChou19@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:47:00 -0400 Subject: [PATCH 1/2] feature: set version 1.43.5-reasoner_cancel-SNAPSHOT for feature/reasoner_cancel --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b3f6f4a..def26e1 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ dev.ikm.tinkar tinkar-schema - 1.43.5-SNAPSHOT + 1.43.5-reasoner_cancel-SNAPSHOT Tinkar Schema: Java https://github.com/IKM/tinkar-proto From ae64589a69e01f6793901b6d34e2793c2cdb81d6 Mon Sep 17 00:00:00 2001 From: Patrick Chou <1326306+pChou19@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:07:19 -0400 Subject: [PATCH 2/2] Explicit cancel reasoner endpoint and allow rejoin --- proto-downstream/ike_admin.proto | 65 ++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/proto-downstream/ike_admin.proto b/proto-downstream/ike_admin.proto index 8132cfc..6f53257 100644 --- a/proto-downstream/ike_admin.proto +++ b/proto-downstream/ike_admin.proto @@ -177,20 +177,57 @@ message RunReasonerResult { string stamp_coordinate_text = 12; string logic_coordinate_text = 13; string edit_coordinate_text = 14; + + /* + * True when the run was stopped by CancelReasoner before it wrote anything. + * success is false and error_message empty: not a success, but not a + * failure either. A cancel that arrives once writing has begun lets the + * write finish, and the run reports success. + */ + bool cancelled = 15; +} + +/* + * First event on every RunReasoner and WatchReasoner stream: which run the + * caller is now watching. + * + * A run belongs to the server, not to the call that started it, so a caller + * may join one already in progress, or one that has already ended. This says + * which, so the client can say "reconnected to a run started at ..." rather + * than implying it just started one. + */ +message ReasonerRunInfo { + // When the run started, in epoch milliseconds. + int64 started_at = 1; + // True if this call started the run; false if it joined one running or finished. + bool started = 2; } /* - * One message in the RunReasoner stream: a phase as it completes, then exactly - * one result. A failure arrives as a result with success=false rather than as a - * stream error, so the caller reads the reason from the same place either way. + * One message in a RunReasoner or WatchReasoner stream: first the run, then + * each phase as it completes (replayed from the start when joining a run + * already under way), then exactly one result. A failure arrives as a result + * with success=false rather than as a stream error, so the caller reads the + * reason from the same place either way. */ message RunReasonerEvent { oneof event { ReasonerPhase phase = 1; RunReasonerResult result = 2; + ReasonerRunInfo run = 3; } } +message WatchReasonerRequest { +} + +message CancelReasonerRequest { +} + +// Empty: the call succeeding is the answer. See CancelReasoner. +message CancelReasonerResponse { +} + // ── Service ───────────────────────────────────────────────────────── service IkeAdmin { @@ -208,6 +245,28 @@ service IkeAdmin { * pipeline itself is a single stateful run on one ReasonerService instance, * so it is NOT exposed as one RPC per phase — that would require the server * to hold a reasoner session between calls. + * + * The run belongs to the server, not to this call. If a run is already + * going, the call joins it instead of starting a second. Ending the call — + * cancelling it, a dropped connection, closing the client — only stops + * watching; the classification carries on. Stop it with CancelReasoner. */ rpc RunReasoner(RunReasonerRequest) returns (stream RunReasonerEvent); + + /* + * Joins the running classification, or replays the most recent one if it + * has finished, as the same stream RunReasoner returns. Never starts a run. + * For getting back to a run after the client was closed. + * + * Fails with NOT_FOUND if there has been no run since the server started. + */ + rpc WatchReasoner(WatchReasonerRequest) returns (stream RunReasonerEvent); + + /* + * Asks the running classification to stop, and returns at once. Everyone + * watching the run gets a result with cancelled=true once it has stopped. + * + * Fails with FAILED_PRECONDITION if no classification is running. + */ + rpc CancelReasoner(CancelReasonerRequest) returns (CancelReasonerResponse); }