Skip to main content

relay_knowledge/application/knowledge/map/
contracts.rs

1//! Public request/response contracts and typed Knowledge Map mutation state.
2
3use serde::Serialize;
4
5use crate::{
6    api::{ApiMetadata, RequestContext},
7    clock::system_now_millis_or_zero,
8    domain::{
9        DirectoryLoadHint, DirectoryUpdateRule, KnowledgeMap, KnowledgeMapHistoryEntry,
10        KnowledgeMapRoute, KnowledgeMapSource, KnowledgeMapSourceKind, KnowledgeMapTopic,
11        RepositoryMapDirectory, RepositoryMapType,
12    },
13};
14
15use super::artifact::{KnowledgeMapArchiveRef, KnowledgeMapHistoryIndexRef};
16
17pub(super) struct MutableKnowledgeMap {
18    pub(super) map_type: RepositoryMapType,
19    pub(super) directories: Vec<RepositoryMapDirectory>,
20    pub(super) map: KnowledgeMap,
21    pub(super) archived_through: u64,
22    pub(super) archive: Option<KnowledgeMapArchiveRef>,
23    pub(super) history_index: Option<KnowledgeMapHistoryIndexRef>,
24    pub(super) requires_publish: bool,
25}
26
27impl MutableKnowledgeMap {
28    pub(super) fn initial(map_type: RepositoryMapType, updated_at: String) -> Self {
29        Self {
30            map: match map_type {
31                RepositoryMapType::Knowledge => KnowledgeMap::initial(updated_at),
32                RepositoryMapType::Codespec => KnowledgeMap::empty(updated_at),
33            },
34            map_type,
35            directories: baseline_directories(map_type),
36            archived_through: 0,
37            archive: None,
38            history_index: None,
39            requires_publish: false,
40        }
41    }
42}
43
44pub(super) fn baseline_directories(map_type: RepositoryMapType) -> Vec<RepositoryMapDirectory> {
45    map_type
46        .required_directories()
47        .iter()
48        .map(|directory| RepositoryMapDirectory {
49            directory: (*directory).to_owned(),
50            purpose: baseline_purpose(map_type, directory).to_owned(),
51            content_scope: vec![format!("{}/{directory}/**", map_type.as_str())],
52            key_files: vec![format!("{}/{directory}/README.md", map_type.as_str())],
53            load_hint: DirectoryLoadHint::OnDemand,
54            relations: Vec::new(),
55            update_rule: DirectoryUpdateRule::Reviewed,
56        })
57        .collect()
58}
59
60fn baseline_purpose(map_type: RepositoryMapType, directory: &str) -> &'static str {
61    match (map_type, directory) {
62        (RepositoryMapType::Codespec, "requirements") => {
63            "Product requirements and acceptance criteria."
64        }
65        (RepositoryMapType::Codespec, "design") => {
66            "Architecture and implementation design records."
67        }
68        (RepositoryMapType::Codespec, "api") => "Public interface and schema contracts.",
69        (RepositoryMapType::Codespec, "test") => "Verification strategy, fixtures, and evidence.",
70        (RepositoryMapType::Codespec, "decisions") => "Durable architecture and product decisions.",
71        (RepositoryMapType::Knowledge, "domain") => "Domain concepts, models, and business rules.",
72        (RepositoryMapType::Knowledge, "guides") => "Task-oriented repository knowledge guides.",
73        (RepositoryMapType::Knowledge, "ops") => "Operational procedures and diagnostics.",
74        (RepositoryMapType::Knowledge, "glossary") => {
75            "Business terminology, aliases, and technical mappings."
76        }
77        (RepositoryMapType::Knowledge, "best-practices") => {
78            "Reviewed engineering and knowledge-management practices."
79        }
80        _ => "Repository navigation knowledge.",
81    }
82}
83
84pub(super) fn metadata(context: &RequestContext) -> ApiMetadata {
85    ApiMetadata::graph_only(context, crate::domain::GraphVersion::ZERO)
86}
87
88pub(super) fn now_stamp() -> String {
89    let seconds = system_now_millis_or_zero() / 1_000;
90    format!("unix:{seconds}")
91}
92
93/// Request to register a source in the repository knowledge map.
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct KnowledgeMapSourceAddRequest {
96    pub id: String,
97    pub topic: String,
98    pub kind: KnowledgeMapSourceKind,
99    pub uri: String,
100    pub source_scope: Option<String>,
101    pub description: Option<String>,
102}
103
104/// Response shared by map mutation commands.
105#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
106pub struct KnowledgeMapMutationResponse {
107    pub metadata: ApiMetadata,
108    pub path: String,
109    pub map_type: RepositoryMapType,
110    pub map_version: u64,
111    pub summary: String,
112}
113
114/// Response returned by read-only map commands.
115#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
116pub struct KnowledgeMapShowResponse {
117    pub metadata: ApiMetadata,
118    pub path: String,
119    pub map_type: RepositoryMapType,
120    pub map: KnowledgeMapView,
121}
122
123/// Bounded assembled view returned by `map show`.
124#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
125pub struct KnowledgeMapView {
126    pub artifact_schema_version: u16,
127    pub map_version: u64,
128    pub updated_at: String,
129    pub directories: Vec<RepositoryMapDirectory>,
130    pub topics: Vec<KnowledgeMapTopic>,
131    pub sources: Vec<KnowledgeMapSource>,
132    pub routes: Vec<KnowledgeMapRoute>,
133    pub history: KnowledgeMapHistoryWindow,
134}
135
136/// Recent history and the checkpoint for history intentionally omitted from a show response.
137#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
138pub struct KnowledgeMapHistoryWindow {
139    pub archived_through: u64,
140    pub complete: bool,
141    pub recent: Vec<KnowledgeMapHistoryEntry>,
142}
143
144/// One explicitly bounded page of complete Knowledge Map history.
145#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
146pub struct KnowledgeMapHistoryResponse {
147    pub metadata: ApiMetadata,
148    pub path: String,
149    pub map_type: RepositoryMapType,
150    pub map_version: u64,
151    pub from_version: u64,
152    pub through_version: u64,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub next_from_version: Option<u64>,
155    pub entries: Vec<KnowledgeMapHistoryEntry>,
156}
157
158/// Response returned by topic routing commands.
159#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
160pub struct KnowledgeMapRouteResponse {
161    pub metadata: ApiMetadata,
162    pub path: String,
163    pub map_type: RepositoryMapType,
164    pub topic: String,
165    pub route: Option<KnowledgeMapRoute>,
166    pub sources: Vec<KnowledgeMapSource>,
167}
168
169/// Response returned by validation commands.
170#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
171pub struct KnowledgeMapValidationResponse {
172    pub metadata: ApiMetadata,
173    pub path: String,
174    pub map_type: RepositoryMapType,
175    pub valid: bool,
176    pub diagnostics: Vec<String>,
177}
178
179/// Response that contains the AGENTS.md reference snippet.
180#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
181pub struct KnowledgeMapAgentSnippetResponse {
182    pub metadata: ApiMetadata,
183    pub snippet: String,
184}