Skip to main content

scc_engine/
ops.rs

1//! Universal operation registry (spec §§6-8): every SCC capability
2//! gets a stable operation ID; `engine.operations().list()` explains the
3//! API and `engine.invoke(id, input)` executes it. Typed namespaces
4//! (`engine.context().task(..)`) and dynamic invoke share ONE
5//! implementation — there are never two paths to the same behavior.
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10// trace:exempt reason=internal-detail
11pub enum MutationClass {
12    Read,
13    Write,
14    Watch,
15    /// Mutates caller-visible session state (ledger visibility, checkpoints).
16    SessionMutation,
17    /// Mutates the indexed model (reindex, ingest, contributions, snapshots).
18    ModelMutation,
19    /// Mutates external/project config (workspace init, plugin lockfile, lessons).
20    ExternalMutation,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24// trace:exempt reason=internal-detail
25pub enum Stability {
26    Stable,
27    Experimental,
28    Internal,
29}
30
31// trace:exempt reason=internal-detail
32pub struct OperationDescriptor {
33    pub id: &'static str,
34    pub description: &'static str,
35    pub mutation: MutationClass,
36    pub streaming: bool,
37    pub stability: Stability,
38}
39
40// trace:exempt reason=internal-detail
41impl serde::Serialize for OperationDescriptor {
42    // trace:exempt reason=internal-detail
43    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44        use serde::ser::SerializeStruct;
45        let mut st = serializer.serialize_struct("OperationDescriptor", 5)?;
46        st.serialize_field("id", self.id)?;
47        st.serialize_field("description", self.description)?;
48        st.serialize_field("mutation", &self.mutation)?;
49        st.serialize_field("streaming", &self.streaming)?;
50        st.serialize_field("stability", &self.stability)?;
51        st.end()
52    }
53}
54
55// trace:exempt reason=internal-detail
56pub const OPERATIONS: &[OperationDescriptor] = &[
57    // workspace
58    OperationDescriptor { id: "operations.list", description: "List every registered operation id (introspection)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
59    OperationDescriptor { id: "operations.describe", description: "Describe one operation by id", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
60    OperationDescriptor { id: "operations.schema", description: "JSON Schema for one operation's input (naming its scc-api request type)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
61    OperationDescriptor { id: "operations.capabilities", description: "Capability vocabulary: permission names, extension points, mutation classes", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
62    OperationDescriptor { id: "workspace.init", description: "Initialize the SCC workspace (.scc/config.yaml + database)", mutation: MutationClass::ExternalMutation, streaming: false , stability: Stability::Stable },
63    OperationDescriptor { id: "workspace.status", description: "Index status, stats, and freshness", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
64    OperationDescriptor { id: "workspace.session", description: "Pin the current model session (repo, revision, epoch, config, plugins, salt)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
65    OperationDescriptor { id: "workspace.session_check", description: "Check a pinned session against live state (current vs stale)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
66    OperationDescriptor { id: "workspace.state_path", description: "Print the SCC state directory", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
67    OperationDescriptor { id: "workspace.scan", description: "Scan explanation: which files index and why", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
68    OperationDescriptor { id: "workspace.languages", description: "Generated language-support matrix", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
69    // index
70    OperationDescriptor { id: "index.full", description: "Index the repository (cold on first run, incremental afterwards)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
71    OperationDescriptor { id: "index.refresh", description: "Refresh selected paths", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
72    OperationDescriptor { id: "index.status", description: "Alias for workspace.status", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
73    OperationDescriptor { id: "index.paths", description: "Alias for index.refresh (refresh selected paths)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
74    OperationDescriptor { id: "index.watch", description: "Watch mode is CLI-local (file watcher loop); not an engine operation", mutation: MutationClass::Read, streaming: false , stability: Stability::Internal },
75    // resolution
76    OperationDescriptor { id: "resolution.run", description: "Run semantic resolution, then recompile the derived layer", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
77    // graph
78    OperationDescriptor { id: "graph.recompile", description: "Recompile the derived graph layer", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
79    OperationDescriptor { id: "graph.search", description: "FTS entity search with LIKE fallback (alias for graph.query entities half)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
80    OperationDescriptor { id: "graph.search_symbols", description: "FTS symbol search with LIKE fallback (alias for graph.query symbols half)", mutation: MutationClass::Read, streaming: false , stability: Stability::Internal },
81    OperationDescriptor { id: "graph.entity.get", description: "Fetch one entity by id", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
82    OperationDescriptor { id: "graph.entities", description: "List components/entities", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
83    OperationDescriptor { id: "graph.relationships", description: "Query relationships", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
84    OperationDescriptor { id: "graph.query", description: "Lexical entity/symbol search with substring fallback", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
85    OperationDescriptor { id: "graph.entity", description: "Alias for graph.entity.get", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
86    OperationDescriptor { id: "graph.traverse", description: "Multi-step directed traversal (out|in|both, predicate + kind filters)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
87    OperationDescriptor { id: "graph.explain", description: "Overlay diagnostics: every assertion behind one edge plus the trusted verdict", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
88    OperationDescriptor { id: "graph.flows", description: "List flows", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
89    // context
90    OperationDescriptor { id: "context.overview", description: "Startup capsule / system overview", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
91    OperationDescriptor { id: "context.atlas", description: "Full System Atlas", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
92    OperationDescriptor { id: "context.startup", description: "Atlas + Surface fusion for session startup", mutation: MutationClass::SessionMutation, streaming: false , stability: Stability::Stable },
93    OperationDescriptor { id: "context.task", description: "Complete task artifact (pack + surface delta)", mutation: MutationClass::SessionMutation, streaming: false , stability: Stability::Stable },
94    OperationDescriptor { id: "context.task_pack", description: "Enriched task pack only (no delta, no ledger)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
95    OperationDescriptor { id: "context.task_delta", description: "Ledger-aware task delta only (records visibility)", mutation: MutationClass::SessionMutation, streaming: false , stability: Stability::Experimental },
96    OperationDescriptor { id: "context.docs", description: "Alias for context.external_docs", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
97    OperationDescriptor { id: "context.subagent", description: "Narrower bounded task pack for delegated agents", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
98    OperationDescriptor { id: "context.component", description: "Context pack for one component", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
99    OperationDescriptor { id: "context.flow", description: "Context pack for one flow", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
100    OperationDescriptor { id: "context.impact", description: "Impact analysis pack", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
101    OperationDescriptor { id: "context.verify", description: "Freshness/evidence verification pack", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
102    OperationDescriptor { id: "context.external_docs", description: "External dependency docs via the configured Context7 command", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
103    OperationDescriptor { id: "context.structural", description: "Structural Source representation", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
104    OperationDescriptor { id: "context.compress", description: "Compress a task pack", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
105    // surface + ranking
106    OperationDescriptor { id: "surface.build", description: "System Surface Map (global or task-personalized)", mutation: MutationClass::Write, streaming: false , stability: Stability::Stable },
107    OperationDescriptor { id: "surface.compile", description: "Compiled Surface Map: unranked candidate entries before PPR/rank/select", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
108    OperationDescriptor { id: "surface.global", description: "Alias for surface.build (global blend, no task)", mutation: MutationClass::Write, streaming: false , stability: Stability::Stable },
109    OperationDescriptor { id: "surface.task", description: "Alias for surface.build (task-personalized blend)", mutation: MutationClass::Write, streaming: false , stability: Stability::Stable },
110    OperationDescriptor { id: "surface.important", description: "Alias for ranking.important", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
111    OperationDescriptor { id: "surface.explain", description: "Alias for ranking.explain", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
112    OperationDescriptor { id: "surface.rank", description: "Alias for ranking.symbols", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
113    OperationDescriptor { id: "surface.select", description: "Alias for selection.budget", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
114    OperationDescriptor { id: "surface.render", description: "Rendered surface text only (no structured result payload)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
115    OperationDescriptor { id: "ranking.important", description: "Fast where-to-pay-attention answer", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
116    OperationDescriptor { id: "ranking.symbols", description: "Full blend per symbol with feature decomposition + plugin hooks", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
117    OperationDescriptor { id: "ranking.global", description: "Alias for ranking.symbols (global blend, no goal)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
118    OperationDescriptor { id: "ranking.task", description: "Alias for ranking.symbols (task-personalized blend)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
119    OperationDescriptor { id: "ranking.entities", description: "Alias for ranking.symbols (entity-ranked blend)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
120    OperationDescriptor { id: "ranking.candidates", description: "Lexical candidate generation (stage 1)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
121    OperationDescriptor { id: "ranking.seeds", description: "Task-seed merge: lexical seeds + plugin seed providers (weight sums by id)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
122    OperationDescriptor { id: "ranking.reference_graph", description: "Normalized reference graph: trusted relationships as (source, kind, target) with provenance/confidence", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
123    OperationDescriptor { id: "ranking.universe", description: "Rank-universe nodes: (id, kind) over the heterogeneous universe in rank order", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
124    OperationDescriptor { id: "ranking.edges", description: "Rank-universe edges: (subject, predicate, object, base weight), pre-aggregation", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
125    OperationDescriptor { id: "ranking.project_symbols", description: "Project a universe vector to per-symbol scores (entity importance to owners)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
126    OperationDescriptor { id: "ranking.features", description: "Per-symbol feature decomposition (core 8 + plugin features) before the blend", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
127    OperationDescriptor { id: "ranking.pagerank.global", description: "Raw global PageRank vector over the heterogeneous universe", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
128    OperationDescriptor { id: "ranking.global_vector", description: "Alias for ranking.pagerank.global (raw global vector)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
129    OperationDescriptor { id: "ranking.pagerank.task", description: "Raw task-personalized PPR vector", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
130    OperationDescriptor { id: "ranking.task_vector", description: "Alias for ranking.pagerank.task (raw task PPR vector)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
131    OperationDescriptor { id: "ranking.final_importance", description: "Pure blend function over explicit features", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
132    OperationDescriptor { id: "ranking.score_entries", description: "Pure per-entry blend over explicit feature rows (batched final_importance)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
133    OperationDescriptor { id: "ranking.edge_weight", description: "Pure edge-weight function", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
134    OperationDescriptor { id: "ranking.architectural_specificity", description: "Architectural specificity (exported/public signals)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
135    OperationDescriptor { id: "ranking.entry", description: "One compiled SurfaceEntry by entry id (full structured candidate)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
136    OperationDescriptor { id: "ranking.trace", description: "Full ranking trace: items plus the seed and required inputs the blend consumed", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
137    OperationDescriptor { id: "ranking.explain", description: "Rank explanation for one symbol", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
138    OperationDescriptor { id: "selection.mmr", description: "MMR diversification over a ranked list", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
139    OperationDescriptor { id: "selection.quotas", description: "Token-fraction quota selection over ranked rows", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
140    OperationDescriptor { id: "selection.budget", description: "Value/token budget selection", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
141    OperationDescriptor { id: "selection.optimize", description: "Alias for selection.budget (budget-optimizer plugin slot)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
142    OperationDescriptor { id: "selection.required", description: "Never-omit entry ids: engine required set + plugin coverage providers", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
143    OperationDescriptor { id: "selection.preview", description: "Per-stage selection survivors (MMR → quotas → budget) over caller rows; default math only", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
144    // source
145    OperationDescriptor { id: "source.structural", description: "Alias for context.structural", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
146    // embeddings (optional semantic ranker)
147    OperationDescriptor { id: "embeddings.build", description: "Compute and store entity embeddings with the configured model", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
148    OperationDescriptor { id: "embeddings.get", description: "Fetch the stored embedding vector for one entity", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
149    OperationDescriptor { id: "embeddings.status", description: "Stored embedding count", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
150    // evidence
151    OperationDescriptor { id: "evidence.get", description: "Fetch one evidence record by id", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
152    OperationDescriptor { id: "evidence.list", description: "List evidence records, optionally filtered by path", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
153    OperationDescriptor { id: "evidence.search", description: "Alias for evidence.list (path-filtered evidence search)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
154    // runtime
155    OperationDescriptor { id: "runtime.ingest", description: "Ingest runtime evidence", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
156    OperationDescriptor { id: "runtime.status", description: "Runtime observation status", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
157    OperationDescriptor { id: "runtime.reconcile", description: "Static-vs-observed reconciliation", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
158    OperationDescriptor { id: "runtime.signatures", description: "Trace-path signatures recorded during OTLP ingest", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
159    // architecture
160    OperationDescriptor { id: "architecture.components", description: "Alias for graph.entities", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
161    OperationDescriptor { id: "architecture.flows", description: "Alias for graph.flows", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
162    OperationDescriptor { id: "architecture.invariants", description: "Structural invariant checks", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
163    OperationDescriptor { id: "architecture.drift", description: "Architectural drift findings", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
164    OperationDescriptor { id: "architecture.cochange", description: "Git co-change pairs", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
165    // history / snapshots / checkpoints
166    OperationDescriptor { id: "history.revisions", description: "Graph revision history", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
167    OperationDescriptor { id: "history.list", description: "Alias for history.revisions", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
168    OperationDescriptor { id: "history.diff", description: "Semantic diff between revisions", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
169    OperationDescriptor { id: "snapshot.save", description: "Save a task snapshot", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
170    OperationDescriptor { id: "snapshot.get", description: "Show a snapshot artifact", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
171    OperationDescriptor { id: "snapshot.diff", description: "Diff a snapshot against current", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
172    OperationDescriptor { id: "checkpoint.save", description: "Capture session checkpoint", mutation: MutationClass::SessionMutation, streaming: false , stability: Stability::Stable },
173    OperationDescriptor { id: "checkpoint.load", description: "Load session checkpoint", mutation: MutationClass::SessionMutation, streaming: false , stability: Stability::Stable },
174    // systems / imports / exports
175    OperationDescriptor { id: "system.stitch", description: "Multi-repo stitch (routes/topics/exports)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
176    OperationDescriptor { id: "import.scip", description: "Import external evidence (SCIP/CCG/GitNexus/Beads...)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
177    OperationDescriptor { id: "import.ccg", description: "Alias for import.scip format=ccg", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
178    OperationDescriptor { id: "import.gitnexus", description: "Alias for import.scip format=gitnexus", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
179    OperationDescriptor { id: "import.tracelayer", description: "Alias for import.scip format=tracelayer", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
180    OperationDescriptor { id: "import.beads", description: "Alias for import.scip format=beads", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
181    OperationDescriptor { id: "import.hindsight", description: "Alias for import.scip format=hindsight", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
182    OperationDescriptor { id: "import.cbm", description: "Alias for import.scip format=cbm", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
183    OperationDescriptor { id: "import.plugin", description: "Import evidence from an evidence-provider plugin: import.<plugin-id> commits the plugin evidence.import batch through validate+commit", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Experimental },
184    OperationDescriptor { id: "export.system_ir", description: "Export System IR (json/jsonl/ccg/flow-graphs)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
185    OperationDescriptor { id: "model.get", description: "Complete live model: repository, epoch, files, entities, relationships, evidence, components, flows, invariants, stats", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
186    OperationDescriptor { id: "model.drift", description: "Alias for architecture.drift", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
187    OperationDescriptor { id: "model.components", description: "Alias for graph.entities", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
188    OperationDescriptor { id: "model.flows", description: "Alias for graph.flows", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
189    OperationDescriptor { id: "model.invariants", description: "Alias for architecture.invariants", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
190    OperationDescriptor { id: "export.system_ir_jsonl", description: "Alias for export.system_ir format=system-ir.jsonl", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
191    OperationDescriptor { id: "export.ccg", description: "Alias for export.system_ir format=ccg", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
192    OperationDescriptor { id: "export.flow_graphs", description: "Alias for export.system_ir format=flow-graphs.json", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
193    OperationDescriptor { id: "export.diagram", description: "Architecture diagram (mermaid|svg) with node/edge/flow counts", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
194    OperationDescriptor { id: "export.snap", description: "Alias for export.system_ir format=capsule.md", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
195    OperationDescriptor { id: "export.capsule", description: "Alias for export.system_ir format=capsule.md", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
196    OperationDescriptor { id: "diagram.render", description: "Alias for export.diagram", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
197    OperationDescriptor { id: "viewer.panels", description: "Plugin viewer data panels (structured title/html + provenance)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
198    OperationDescriptor { id: "viewer.snapshot", description: "Viewer snapshots are CLI-local browser capture; not an engine operation", mutation: MutationClass::Read, streaming: false , stability: Stability::Internal },
199    // integrity / integrations / lessons / setup
200    OperationDescriptor { id: "integrity.invariants", description: "Alias for architecture.invariants", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
201    OperationDescriptor { id: "integrity.ci", description: "CI gate over drift severity", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
202    OperationDescriptor { id: "integrations.list", description: "Configured adapters with capability scope", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
203    OperationDescriptor { id: "integration.list", description: "Alias for integrations.list", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
204    OperationDescriptor { id: "integration.doctor", description: "Alias for integrations.doctor", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
205    OperationDescriptor { id: "integrations.doctor", description: "Offline adapter diagnostics", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
206    OperationDescriptor { id: "integrations.describe", description: "Describe one integration adapter by name", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
207    OperationDescriptor { id: "lessons.add", description: "Append a hindsight lesson", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
208    OperationDescriptor { id: "lessons.list", description: "List hindsight lessons", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
209    OperationDescriptor { id: "beads.list", description: "List active bead tasks", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
210    OperationDescriptor { id: "beads.active", description: "Alias for beads.list", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
211    OperationDescriptor { id: "plugins.list", description: "List enabled plugins with lock entries", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
212    OperationDescriptor { id: "plugins.describe", description: "Describe one plugin manifest", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
213    OperationDescriptor { id: "plugins.doctor", description: "Plugin environment + failure diagnostics", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
214    OperationDescriptor { id: "plugins.invoke", description: "Invoke any plugin operation explicitly", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
215    OperationDescriptor { id: "plugins.lock", description: "Write .scc/plugins.lock from the live plugin set", mutation: MutationClass::ExternalMutation, streaming: false , stability: Stability::Stable },
216    OperationDescriptor { id: "plugins.check", description: "Verify live plugins against .scc/plugins.lock", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
217    OperationDescriptor { id: "plugins.enable", description: "Add a plugin id to the project allow-list (plugins.enabled in .scc/config.yaml)", mutation: MutationClass::ExternalMutation, streaming: false , stability: Stability::Experimental },
218    OperationDescriptor { id: "plugins.disable", description: "Remove a plugin id from the project allow-list (plugins.enabled in .scc/config.yaml)", mutation: MutationClass::ExternalMutation, streaming: false , stability: Stability::Experimental },
219    OperationDescriptor { id: "plugins.graph", description: "Deterministic extension order per type (priority + before/after DAG)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
220    OperationDescriptor { id: "plugins.contribute", description: "Validate and commit a plugin contribution batch (entities, relationships, evidence)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
221    OperationDescriptor { id: "plugins.promote", description: "Promote selected sidecar findings to canonical relationships (core predicates, existing endpoints, provenance-stamped)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Experimental },
222    OperationDescriptor { id: "plugin_state.get", description: "Read one namespaced plugin state key (StateRead grant)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
223    OperationDescriptor { id: "plugin_state.put", description: "Write one namespaced plugin state key (StateWrite grant)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
224    OperationDescriptor { id: "plugin_state.delete", description: "Delete one namespaced plugin state key (StateWrite grant)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Stable },
225    OperationDescriptor { id: "plugin_state.scan", description: "Scan namespaced plugin state keys by prefix (StateRead grant)", mutation: MutationClass::Read, streaming: false , stability: Stability::Stable },
226    OperationDescriptor { id: "sidecar.put", description: "Write one raw sidecar fact under (plugin, graph, key) (StateWrite grant; never authoritative)", mutation: MutationClass::ModelMutation, streaming: false , stability: Stability::Experimental },
227    OperationDescriptor { id: "sidecar.get", description: "Read one raw sidecar fact (StateRead grant)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
228    OperationDescriptor { id: "sidecar.scan", description: "Scan raw sidecar facts by prefix within one plugin graph (StateRead grant)", mutation: MutationClass::Read, streaming: false , stability: Stability::Experimental },
229];
230
231// trace:exempt reason=internal-detail
232pub fn describe(id: &str) -> Option<&'static OperationDescriptor> {
233    OPERATIONS.iter().find(|d| d.id == id)
234}
235
236// trace:exempt reason=internal-detail
237pub fn ids() -> Vec<&'static str> {
238    OPERATIONS.iter().map(|d| d.id).collect()
239}
240
241/// JSON Schema for one operation's input (§9): generated from the canonical
242/// scc-api request type via schemars — the same struct invoke deserializes,
243/// so the schema can never drift from the implementation. Alias ops share
244/// their canonical op's schema. Returns None for unknown ids.
245// trace:v1 id=impl.scc-engine-ops.input-schema work=WORK-SI-MMMJA4G6 satisfies=REQ-SI-503JSBGP
246pub fn input_schema(id: &str) -> Option<serde_json::Value> {
247    use scc_api::*;
248    // Operational envelope inputs (no dedicated request struct): free-form
249    // object with the documented fields.
250    let free = |props: &[(&str, &str)]| {
251        let mut m = serde_json::Map::new();
252        for (k, t) in props {
253            m.insert(k.to_string(), serde_json::json!({"type": t}));
254        }
255        serde_json::json!({"type": "object", "properties": m})
256    };
257    let v: serde_json::Value = match id {
258        "context.task" | "context.task_pack" | "context.task_delta" => {
259            serde_json::to_value(schemars::schema_for!(TaskContextRequest)).unwrap_or(serde_json::json!({}))
260        }
261        "context.startup" => serde_json::to_value(schemars::schema_for!(StartupRequest)).unwrap_or(serde_json::json!({})),
262        "surface.build" | "surface.global" | "surface.task" | "surface.render" | "ranking.important" | "surface.important" => {
263            serde_json::to_value(schemars::schema_for!(SurfaceRequest)).unwrap_or(serde_json::json!({}))
264        }
265        "context.component" | "context.flow" => serde_json::to_value(schemars::schema_for!(DetailRequest)).unwrap_or(serde_json::json!({})),
266        "surface.compile" => free(&[]),
267        "context.impact" => serde_json::to_value(schemars::schema_for!(ImpactRequest)).unwrap_or(serde_json::json!({})),
268        "context.structural" | "source.structural" => {
269            serde_json::to_value(schemars::schema_for!(StructuralRequest)).unwrap_or(serde_json::json!({}))
270        }
271        "graph.query" | "graph.search" | "graph.search_symbols" => {
272            serde_json::to_value(schemars::schema_for!(QueryRequest)).unwrap_or(serde_json::json!({}))
273        }
274        "graph.traverse" => serde_json::to_value(schemars::schema_for!(TraverseRequest)).unwrap_or(serde_json::json!({})),
275        "graph.explain" => free(&[("subject", "string"), ("predicate", "string"), ("object", "string")]),
276        "plugins.describe" => free(&[("id", "string")]),
277        "plugins.inspect" => free(&[("id", "string")]),
278        "plugins.enable" | "plugins.disable" => free(&[("id", "string")]),
279        "graph.entity.get" | "graph.entity" => free(&[("id", "string")]),
280        "ranking.symbols" | "ranking.global" | "ranking.task" | "ranking.entities" | "surface.rank" => {
281            serde_json::to_value(schemars::schema_for!(RankRequest)).unwrap_or(serde_json::json!({}))
282        }
283        _ => return None,
284    };
285    Some(serde_json::json!({"operation": id, "input": v}))
286}
287
288/// Capability vocabulary (§9): the permission names plugins request, the
289/// extension-point names they register under, and the mutation classes
290/// operations declare. Derived from the [`scc_plugin_api::Permission`] enum
291/// (single source) so docs can never drift from enforcement.
292// trace:v1 id=impl.scc-engine-ops.capabilities work=WORK-SI-MMMJA4G6 satisfies=REQ-SI-503JSBGP
293pub fn capabilities() -> serde_json::Value {
294    serde_json::json!({
295        "permissions": [
296            {"id": "repo.read", "description": "Read repository files"},
297            {"id": "repo.write", "description": "Write repository files"},
298            {"id": "graph.read", "description": "Read the Reality Graph (raw + trusted)"},
299            {"id": "graph.contribute", "description": "Contribute entities/relationships/evidence"},
300            {"id": "evidence.contribute", "description": "Alias scope for graph.contribute (evidence importers)"},
301            {"id": "runtime.contribute", "description": "Contribute runtime observations"},
302            {"id": "state.read", "description": "Read namespaced plugin state"},
303            {"id": "state.write", "description": "Write namespaced plugin state"},
304            {"id": "network", "description": "Network access"},
305            {"id": "subprocess", "description": "Spawn subprocesses"},
306            {"id": "operation.register", "description": "Register custom operations (via manifest operations list)"},
307            {"id": "ranking.extend", "description": "Ranking hooks: seeds, candidates, features, rank edges, criticality, novelty, risk, semantic, weights, rerank, similarity, profiles, coverage"},
308            {"id": "context.extend", "description": "Context sections: task sections, startup sections, verify diagnostics"},
309            {"id": "renderer.extend", "description": "Export/diagram rendering via export.* operations and exporter:<format> extensions"},
310        ],
311        "extension_points": [
312            "candidate-provider", "seed-provider", "rank-feature", "rank-edge", "criticality-provider", "novelty-provider", "risk-provider", "semantic-provider", "runtime-evidence", "edge-weight",
313            "reranker", "similarity", "blend-profile", "coverage", "verify-diagnostic",
314            "context-section", "startup-section", "exporter", "viewer-panel",
315            "quota-policy", "budget-optimizer", "diversity-policy",
316            "operation",
317        ],
318        "mutation_classes": ["Read", "Write", "Watch"],
319        "plugin_api_version": scc_plugin_api::PLUGIN_API_VERSION,
320        "api_version": scc_api::API_VERSION,
321    })
322}