@@ -14,15 +14,32 @@ import (
1414
1515// workspaceEvidenceFromContext builds evidence metadata for namespace-scoped graph queries.
1616// @intent include workspace path and git state when available so LLM has traceable provenance.
17- func (h * handlers ) workspaceEvidenceFromContext (ctx context.Context ) map [ string ] any {
17+ func (h * handlers ) workspaceEvidenceFromContext (ctx context.Context ) workspaceEvidenceBlock {
1818 ns := ctxns .FromContext (ctx )
1919 return h .workspaceEvidence (ns )
2020}
2121
22+ // workspaceEvidenceBlock captures stable workspace provenance fields shared across MCP responses.
23+ // @intent keep evidence payloads typed while preserving the legacy namespace/workspace/git JSON shape.
24+ type workspaceEvidenceBlock struct {
25+ Namespace string `json:"namespace"`
26+ WorkspacePath string `json:"workspace_path,omitempty"`
27+ Git * workspaceGitEvidenceBlock `json:"git,omitempty"`
28+ }
29+
30+ // workspaceGitEvidenceBlock captures git provenance attached to workspace evidence.
31+ // @intent preserve the legacy git evidence keys while making nil-versus-false behavior explicit.
32+ type workspaceGitEvidenceBlock struct {
33+ Branch string `json:"branch"`
34+ Commit string `json:"commit"`
35+ Dirty * bool `json:"dirty,omitempty"`
36+ Remote string `json:"remote,omitempty"`
37+ }
38+
2239// @intent collect namespace-scoped workspace and git provenance so MCP responses can explain where graph evidence came from.
23- func (h * handlers ) workspaceEvidence (namespace string ) map [ string ] any {
40+ func (h * handlers ) workspaceEvidence (namespace string ) workspaceEvidenceBlock {
2441 ns := ctxns .Normalize (namespace )
25- evidence := map [ string ] any { "namespace" : ns }
42+ evidence := workspaceEvidenceBlock { Namespace : ns }
2643
2744 root := h .deps .WorkspaceRoot
2845 if root == "" {
@@ -35,17 +52,17 @@ func (h *handlers) workspaceEvidence(namespace string) map[string]any {
3552 return evidence
3653 }
3754
38- evidence [ "workspace_path" ] = workspacePath
55+ evidence . WorkspacePath = workspacePath
3956
4057 gitInfo := workspaceGitEvidence (workspacePath )
41- if len ( gitInfo ) > 0 {
42- evidence [ "git" ] = gitInfo
58+ if gitInfo != nil {
59+ evidence . Git = gitInfo
4360 }
4461 return evidence
4562}
4663
4764// @intent summarize git branch, commit, remote, and dirty state for workspace-scoped evidence blocks.
48- func workspaceGitEvidence (path string ) map [ string ] any {
65+ func workspaceGitEvidence (path string ) * workspaceGitEvidenceBlock {
4966 repo , err := git .PlainOpenWithOptions (path , & git.PlainOpenOptions {DetectDotGit : true })
5067 if err != nil {
5168 return nil
@@ -56,15 +73,16 @@ func workspaceGitEvidence(path string) map[string]any {
5673 return nil
5774 }
5875
59- info := map [ string ] any {
60- "branch" : branchNameForRef (head .Name ()),
61- "commit" : head .Hash ().String (),
76+ info := & workspaceGitEvidenceBlock {
77+ Branch : branchNameForRef (head .Name ()),
78+ Commit : head .Hash ().String (),
6279 }
6380
6481 wt , err := repo .Worktree ()
6582 if err == nil {
6683 if status , err := wt .Status (); err == nil {
67- info ["dirty" ] = ! status .IsClean ()
84+ dirty := ! status .IsClean ()
85+ info .Dirty = & dirty
6886 }
6987 }
7088
@@ -83,16 +101,13 @@ func workspaceGitEvidence(path string) map[string]any {
83101 remoteURL = r .URLs [0 ]
84102 break
85103 }
104+ }
105+ }
106+ if remoteURL != "" {
107+ info .Remote = remoteURL
86108 }
87109 }
88- if remoteURL != "" {
89- info ["remote" ] = remoteURL
90- }
91- }
92110
93- if len (info ) == 0 {
94- return nil
95- }
96111 return info
97112}
98113
0 commit comments