fix: remove workflow completion surface
This commit is contained in:
parent
d30dca2d99
commit
0b56052a44
|
|
@ -2,7 +2,7 @@
|
||||||
title: 'Remove workflow tracking and workflow resources'
|
title: 'Remove workflow tracking and workflow resources'
|
||||||
state: 'inprogress'
|
state: 'inprogress'
|
||||||
created_at: '2026-07-15T19:02:13Z'
|
created_at: '2026-07-15T19:02:13Z'
|
||||||
updated_at: '2026-07-15T21:04:19Z'
|
updated_at: '2026-07-15T21:22:40Z'
|
||||||
assignee: null
|
assignee: null
|
||||||
queued_by: 'workspace-panel'
|
queued_by: 'workspace-panel'
|
||||||
queued_at: '2026-07-15T19:54:36Z'
|
queued_at: '2026-07-15T19:54:36Z'
|
||||||
|
|
|
||||||
|
|
@ -182,4 +182,31 @@ Validation:
|
||||||
- nix build .#yoi --no-link -> exit 0
|
- nix build .#yoi --no-link -> exit 0
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- event: implementation_report author: hare at: 2026-07-15T21:22:40Z -->
|
||||||
|
|
||||||
|
## Implementation report
|
||||||
|
|
||||||
|
Follow-up fix for focused re-review blocker.
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
- Removed `/` as a web console completion sigil; composer completions now only recognize commands, file refs, and Knowledge refs.
|
||||||
|
- Removed `workflow` from the web console completion result type and route-side completion response typing.
|
||||||
|
- Updated web completion tests so slash text remains plain text/no completion token.
|
||||||
|
- Removed stale Workflow completion/resolver wording from protocol and nearby comments.
|
||||||
|
- Did not add hidden Workflow compatibility or broad Skills replacement.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- rg "ActiveWorkflow|active_workflow|Active workflow" -> no matches (exit 1)
|
||||||
|
- focused completion-surface rg for workflow_invoke / workflow completion kind / slash Workflow resolver wording / resources/workflows / .yoi/workflow / Resident workflows -> no matches (exit 1)
|
||||||
|
- focused web/protocol rg "kind.*workflow" -> no matches (exit 1); remaining project-wide "kind: workflow" mentions are only legacy persisted SystemItem ignore tests from the bounded compatibility path.
|
||||||
|
- git diff --check -> exit 0
|
||||||
|
- cd web/workspace && deno task check && deno task test -> exit 0
|
||||||
|
- cargo test -p yoi --tests -> exit 0
|
||||||
|
- cargo check -p yoi -> exit 0
|
||||||
|
- yoi ticket doctor -> exit 0
|
||||||
|
- nix build .#yoi --no-link -> exit 0
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,8 @@ pub struct WorkerManifest {
|
||||||
/// `[feature.web].enabled = true`.
|
/// `[feature.web].enabled = true`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub web: Option<WebConfig>,
|
pub web: Option<WebConfig>,
|
||||||
/// External Agent Skills (`SKILL.md`) directories to ingest as
|
/// External Agent Skills (`SKILL.md`) candidate directories. Each entry
|
||||||
/// Workflows. Each entry is a path to a skills *root* (i.e. a
|
/// is a path to a skills *root* (i.e. a
|
||||||
/// directory whose children are individual `<name>/SKILL.md` skill
|
/// directory whose children are individual `<name>/SKILL.md` skill
|
||||||
/// bundles). Paths are resolved against the manifest's base
|
/// bundles). Paths are resolved against the manifest's base
|
||||||
/// directory like other path fields. Absent ⇒ no skills loaded;
|
/// directory like other path fields. Absent ⇒ no skills loaded;
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,7 @@ pub enum Method {
|
||||||
///
|
///
|
||||||
/// Reply is sent on the same socket as `Event::Completions` (not
|
/// Reply is sent on the same socket as `Event::Completions` (not
|
||||||
/// broadcast). The IPC server handles this directly and writes
|
/// broadcast). The IPC server handles this directly and writes
|
||||||
/// the response straight back to the requesting socket. Empty
|
/// the response straight back to the requesting socket.
|
||||||
/// results for resolvers that are not yet wired up
|
|
||||||
/// (Knowledge / Workflow).
|
|
||||||
ListCompletions {
|
ListCompletions {
|
||||||
kind: CompletionKind,
|
kind: CompletionKind,
|
||||||
prefix: String,
|
prefix: String,
|
||||||
|
|
@ -601,11 +599,7 @@ pub enum AlertSource {
|
||||||
|
|
||||||
/// Kind of completion requested by `Method::ListCompletions`.
|
/// Kind of completion requested by `Method::ListCompletions`.
|
||||||
///
|
///
|
||||||
/// Mirrors the TUI prefix sigils: `@` → `File`, `#` → `Knowledge`,
|
/// Mirrors the completion prefix sigils: `@` → `File`, `#` → `Knowledge`.
|
||||||
/// `/` → `Workflow`. Knowledge and Workflow resolvers are currently
|
|
||||||
/// stubs (always reply with empty `entries`); the wire shape is
|
|
||||||
/// nailed down here so the TUI side can ship without waiting for
|
|
||||||
/// the memory / workflow tickets.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
|
@ -616,7 +610,7 @@ pub enum CompletionKind {
|
||||||
|
|
||||||
/// One candidate returned in `Event::Completions::entries`.
|
/// One candidate returned in `Event::Completions::entries`.
|
||||||
///
|
///
|
||||||
/// `value` is a path (file kind) or a slug (knowledge / workflow).
|
/// `value` is a path (file kind) or a Knowledge slug.
|
||||||
/// `is_dir` is meaningful only for the file kind — it lets the TUI
|
/// `is_dir` is meaningful only for the file kind — it lets the TUI
|
||||||
/// keep a trailing `/` after a directory selection so the user can
|
/// keep a trailing `/` after a directory selection so the user can
|
||||||
/// drill in without re-typing the prefix.
|
/// drill in without re-typing the prefix.
|
||||||
|
|
|
||||||
|
|
@ -607,8 +607,7 @@ impl App {
|
||||||
/// suggestion" — partial typing like `@README.` followed by
|
/// suggestion" — partial typing like `@README.` followed by
|
||||||
/// Enter should chip when the popup is on `README.md`.
|
/// Enter should chip when the popup is on `README.md`.
|
||||||
///
|
///
|
||||||
/// Files (and Knowledge / Workflow entries, which have no dir
|
/// Files and Knowledge entries chipify here. Directory file entries return `false`
|
||||||
/// concept) chipify here. Directory file entries return `false`
|
|
||||||
/// so the caller can fall through to `apply_completion_text`
|
/// so the caller can fall through to `apply_completion_text`
|
||||||
/// for drill-in — chip-ifying a directory on Enter would strand
|
/// for drill-in — chip-ifying a directory on Enter would strand
|
||||||
/// the user with no way to inspect children.
|
/// the user with no way to inspect children.
|
||||||
|
|
|
||||||
|
|
@ -833,7 +833,7 @@ impl<C: LlmClient, St: Store> Worker<C, St> {
|
||||||
///
|
///
|
||||||
/// Default `true`: normal Workers may expose each resident section according
|
/// Default `true`: normal Workers may expose each resident section according
|
||||||
/// to its own gate and manifest settings. Internal disposable workers set
|
/// to its own gate and manifest settings. Internal disposable workers set
|
||||||
/// this to `false` so summary, Knowledge, and Workflow residency are all
|
/// this to `false` so summary and Knowledge residency are both
|
||||||
/// suppressed while explicit tools remain available.
|
/// suppressed while explicit tools remain available.
|
||||||
pub fn set_resident_injection(&mut self, enabled: bool) {
|
pub fn set_resident_injection(&mut self, enabled: bool) {
|
||||||
self.inject_resident_summary = enabled;
|
self.inject_resident_summary = enabled;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { buildComposerRequest, parseSigilSegments } from "./composer-command.ts";
|
import {
|
||||||
|
buildComposerRequest,
|
||||||
|
parseSigilSegments,
|
||||||
|
} from "./composer-command.ts";
|
||||||
|
|
||||||
declare const Deno: {
|
declare const Deno: {
|
||||||
test(name: string, fn: () => void): void;
|
test(name: string, fn: () => void): void;
|
||||||
|
|
@ -19,13 +22,16 @@ function assertEquals<T>(actual: T, expected: T): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
Deno.test("parseSigilSegments converts TUI-style references", () => {
|
Deno.test("parseSigilSegments converts TUI-style references", () => {
|
||||||
assertEquals(parseSigilSegments("read @src/main.rs then #memory and /workflow"), [
|
assertEquals(
|
||||||
{ kind: "text", content: "read " },
|
parseSigilSegments("read @src/main.rs then #memory and /literal"),
|
||||||
{ kind: "file_ref", path: "src/main.rs" },
|
[
|
||||||
{ kind: "text", content: " then " },
|
{ kind: "text", content: "read " },
|
||||||
{ kind: "knowledge_ref", slug: "memory" },
|
{ kind: "file_ref", path: "src/main.rs" },
|
||||||
{ kind: "text", content: " and /workflow" },
|
{ kind: "text", content: " then " },
|
||||||
]);
|
{ kind: "knowledge_ref", slug: "memory" },
|
||||||
|
{ kind: "text", content: " and /literal" },
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("buildComposerRequest sends user segments when sigils are present", () => {
|
Deno.test("buildComposerRequest sends user segments when sigils are present", () => {
|
||||||
|
|
@ -51,7 +57,10 @@ Deno.test("buildComposerRequest parses colon commands", () => {
|
||||||
const help = buildComposerRequest(":help compact");
|
const help = buildComposerRequest(":help compact");
|
||||||
assert(help.ok, "help should be accepted");
|
assert(help.ok, "help should be accepted");
|
||||||
assertEquals(help.request, undefined);
|
assertEquals(help.request, undefined);
|
||||||
assert(help.notice?.includes(":compact"), "help should return a local notice");
|
assert(
|
||||||
|
help.notice?.includes(":compact"),
|
||||||
|
"help should return a local notice",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("buildComposerRequest rejects invalid colon commands", () => {
|
Deno.test("buildComposerRequest rejects invalid colon commands", () => {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ Deno.test("completionTokenAt detects TUI-style sigils before the cursor", () =>
|
||||||
});
|
});
|
||||||
assertEquals(completionTokenAt(":comp", 5)?.kind, "command");
|
assertEquals(completionTokenAt(":comp", 5)?.kind, "command");
|
||||||
assertEquals(completionTokenAt("ask #mem", 8)?.kind, "knowledge");
|
assertEquals(completionTokenAt("ask #mem", 8)?.kind, "knowledge");
|
||||||
assertEquals(completionTokenAt("run /work", 9)?.kind, "workflow");
|
assertEquals(completionTokenAt("run /work", 9), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("applyCompletion replaces the active token and advances the cursor", () => {
|
Deno.test("applyCompletion replaces the active token and advances the cursor", () => {
|
||||||
|
|
@ -50,5 +50,7 @@ Deno.test("applyCompletion replaces the active token and advances the cursor", (
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("localCommandCompletions filters colon commands", () => {
|
Deno.test("localCommandCompletions filters colon commands", () => {
|
||||||
assertEquals(localCommandCompletions("com").map((entry) => entry.value), ["compact"]);
|
assertEquals(localCommandCompletions("com").map((entry) => entry.value), [
|
||||||
|
"compact",
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export type ComposerCompletionKind = "command" | "file" | "knowledge" | "workflow";
|
export type ComposerCompletionKind = "command" | "file" | "knowledge";
|
||||||
|
|
||||||
export type ComposerCompletionToken = {
|
export type ComposerCompletionToken = {
|
||||||
sigil: ":" | "@" | "#" | "/";
|
sigil: ":" | "@" | "#";
|
||||||
kind: ComposerCompletionKind;
|
kind: ComposerCompletionKind;
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
|
|
@ -35,7 +35,7 @@ export function completionTokenAt(
|
||||||
): ComposerCompletionToken | null {
|
): ComposerCompletionToken | null {
|
||||||
const boundedCursor = Math.max(0, Math.min(cursor, value.length));
|
const boundedCursor = Math.max(0, Math.min(cursor, value.length));
|
||||||
const before = value.slice(0, boundedCursor);
|
const before = value.slice(0, boundedCursor);
|
||||||
const match = /(^|\s)([:@#/])([^\s]*)$/.exec(before);
|
const match = /(^|\s)([:@#])([^\s]*)$/.exec(before);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +51,9 @@ export function completionTokenAt(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function localCommandCompletions(prefix: string): ComposerCompletionEntry[] {
|
export function localCommandCompletions(
|
||||||
|
prefix: string,
|
||||||
|
): ComposerCompletionEntry[] {
|
||||||
const normalized = prefix.toLowerCase();
|
const normalized = prefix.toLowerCase();
|
||||||
return COLON_COMMAND_COMPLETIONS.filter((entry) =>
|
return COLON_COMMAND_COMPLETIONS.filter((entry) =>
|
||||||
entry.value.toLowerCase().startsWith(normalized)
|
entry.value.toLowerCase().startsWith(normalized)
|
||||||
|
|
@ -65,13 +67,19 @@ export function applyCompletion(
|
||||||
): CompletionApplyResult {
|
): CompletionApplyResult {
|
||||||
const suffix = entry.is_dir ? "/" : " ";
|
const suffix = entry.is_dir ? "/" : " ";
|
||||||
const replacement = `${token.sigil}${entry.value}${suffix}`;
|
const replacement = `${token.sigil}${entry.value}${suffix}`;
|
||||||
const restStart = !entry.is_dir && value[token.end] === " " ? token.end + 1 : token.end;
|
const restStart = !entry.is_dir && value[token.end] === " "
|
||||||
const next = `${value.slice(0, token.start)}${replacement}${value.slice(restStart)}`;
|
? token.end + 1
|
||||||
|
: token.end;
|
||||||
|
const next = `${value.slice(0, token.start)}${replacement}${
|
||||||
|
value.slice(restStart)
|
||||||
|
}`;
|
||||||
const cursor = token.start + replacement.length;
|
const cursor = token.start + replacement.length;
|
||||||
return { value: next, cursor };
|
return { value: next, cursor };
|
||||||
}
|
}
|
||||||
|
|
||||||
function completionKindForSigil(sigil: ComposerCompletionToken["sigil"]): ComposerCompletionKind {
|
function completionKindForSigil(
|
||||||
|
sigil: ComposerCompletionToken["sigil"],
|
||||||
|
): ComposerCompletionKind {
|
||||||
switch (sigil) {
|
switch (sigil) {
|
||||||
case ":":
|
case ":":
|
||||||
return "command";
|
return "command";
|
||||||
|
|
@ -79,7 +87,5 @@ function completionKindForSigil(sigil: ComposerCompletionToken["sigil"]): Compos
|
||||||
return "file";
|
return "file";
|
||||||
case "#":
|
case "#":
|
||||||
return "knowledge";
|
return "knowledge";
|
||||||
case "/":
|
|
||||||
return "workflow";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkerCompletionsResult = {
|
type WorkerCompletionsResult = {
|
||||||
kind: "file" | "knowledge" | "workflow";
|
kind: "file" | "knowledge";
|
||||||
prefix: string;
|
prefix: string;
|
||||||
entries: ComposerCompletionEntry[];
|
entries: ComposerCompletionEntry[];
|
||||||
diagnostics: Diagnostic[];
|
diagnostics: Diagnostic[];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user