1use 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::ARTIFACT_SCHEMA_VERSION;
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) omitted_through: u64,
22 pub(super) requires_publish: bool,
23 pub(super) legacy_glossary_uri_normalized: bool,
24}
25
26impl MutableKnowledgeMap {
27 pub(super) fn initial(map_type: RepositoryMapType, updated_at: String) -> Self {
28 Self {
29 map: match map_type {
30 RepositoryMapType::Knowledge => KnowledgeMap::initial(updated_at),
31 RepositoryMapType::Codespec => KnowledgeMap::empty(updated_at),
32 },
33 map_type,
34 directories: baseline_directories(map_type),
35 omitted_through: 0,
36 requires_publish: false,
37 legacy_glossary_uri_normalized: false,
38 }
39 }
40
41 pub(super) fn record_required_publication(
42 &mut self,
43 source_schema_version: u16,
44 updated_at: String,
45 ) -> String {
46 let glossary_only =
47 source_schema_version == ARTIFACT_SCHEMA_VERSION && self.legacy_glossary_uri_normalized;
48 let (action, history_summary, response_summary) = if glossary_only {
49 (
50 "source.migrate",
51 "Migrated the reserved business glossary source URI to the canonical artifact.",
52 "migrated Knowledge Map legacy glossary URI to the canonical artifact",
53 )
54 } else {
55 (
56 "history.compact",
57 "Migrated repository map to bounded recent-only history storage.",
58 "migrated repository map to schema v4 recent-only history",
59 )
60 };
61 self.map
62 .record_change(action, history_summary.to_owned(), updated_at);
63 response_summary.to_owned()
64 }
65}
66
67pub(super) fn baseline_directories(map_type: RepositoryMapType) -> Vec<RepositoryMapDirectory> {
68 map_type
69 .required_directories()
70 .iter()
71 .map(|directory| RepositoryMapDirectory {
72 directory: (*directory).to_owned(),
73 purpose: baseline_purpose(map_type, directory).to_owned(),
74 content_scope: vec![format!("{}/{directory}/**", map_type.as_str())],
75 key_files: vec![format!("{}/{directory}/README.md", map_type.as_str())],
76 load_hint: DirectoryLoadHint::OnDemand,
77 relations: Vec::new(),
78 update_rule: DirectoryUpdateRule::Reviewed,
79 })
80 .collect()
81}
82
83fn baseline_purpose(map_type: RepositoryMapType, directory: &str) -> &'static str {
84 match (map_type, directory) {
85 (RepositoryMapType::Codespec, "requirements") => {
86 "Product requirements and acceptance criteria."
87 }
88 (RepositoryMapType::Codespec, "design") => {
89 "Architecture and implementation design records."
90 }
91 (RepositoryMapType::Codespec, "api") => "Public interface and schema contracts.",
92 (RepositoryMapType::Codespec, "test") => "Verification strategy, fixtures, and evidence.",
93 (RepositoryMapType::Codespec, "decisions") => "Durable architecture and product decisions.",
94 (RepositoryMapType::Knowledge, "domain") => "Domain concepts, models, and business rules.",
95 (RepositoryMapType::Knowledge, "guides") => "Task-oriented repository knowledge guides.",
96 (RepositoryMapType::Knowledge, "ops") => "Operational procedures and diagnostics.",
97 (RepositoryMapType::Knowledge, "glossary") => {
98 "Business terminology, aliases, and technical mappings."
99 }
100 (RepositoryMapType::Knowledge, "best-practices") => {
101 "Reviewed engineering and knowledge-management practices."
102 }
103 _ => "Repository navigation knowledge.",
104 }
105}
106
107pub(super) fn metadata(context: &RequestContext) -> ApiMetadata {
108 ApiMetadata::graph_only(context, crate::domain::GraphVersion::ZERO)
109}
110
111pub(super) fn now_stamp() -> String {
112 let seconds = system_now_millis_or_zero() / 1_000;
113 format!("unix:{seconds}")
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct KnowledgeMapSourceAddRequest {
119 pub id: String,
120 pub topic: String,
121 pub kind: KnowledgeMapSourceKind,
122 pub uri: String,
123 pub source_scope: Option<String>,
124 pub description: Option<String>,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
129pub struct KnowledgeMapMutationResponse {
130 pub metadata: ApiMetadata,
131 pub path: String,
132 pub map_type: RepositoryMapType,
133 pub map_version: u64,
134 pub summary: String,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
139pub struct KnowledgeMapShowResponse {
140 pub metadata: ApiMetadata,
141 pub path: String,
142 pub map_type: RepositoryMapType,
143 pub map: KnowledgeMapView,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
148pub struct KnowledgeMapView {
149 pub artifact_schema_version: u16,
150 pub map_version: u64,
151 pub updated_at: String,
152 pub directories: Vec<RepositoryMapDirectory>,
153 pub topics: Vec<KnowledgeMapTopic>,
154 pub sources: Vec<KnowledgeMapSource>,
155 pub routes: Vec<KnowledgeMapRoute>,
156 pub history: KnowledgeMapHistoryWindow,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
161pub struct KnowledgeMapHistoryWindow {
162 pub omitted_through: u64,
163 pub complete: bool,
164 pub recent: Vec<KnowledgeMapHistoryEntry>,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
169pub struct KnowledgeMapHistoryResponse {
170 pub metadata: ApiMetadata,
171 pub path: String,
172 pub map_type: RepositoryMapType,
173 pub map_version: u64,
174 pub omitted_through: u64,
175 pub earliest_available_version: u64,
176 pub from_version: u64,
177 pub through_version: u64,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub next_from_version: Option<u64>,
180 pub entries: Vec<KnowledgeMapHistoryEntry>,
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
185pub struct KnowledgeMapRouteResponse {
186 pub metadata: ApiMetadata,
187 pub path: String,
188 pub map_type: RepositoryMapType,
189 pub topic: String,
190 pub route: Option<KnowledgeMapRoute>,
191 pub sources: Vec<KnowledgeMapSource>,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
196pub struct KnowledgeMapValidationResponse {
197 pub metadata: ApiMetadata,
198 pub path: String,
199 pub map_type: RepositoryMapType,
200 pub valid: bool,
201 pub diagnostics: Vec<String>,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
206pub struct KnowledgeMapAgentSnippetResponse {
207 pub metadata: ApiMetadata,
208 pub snippet: String,
209}