@@ -109,6 +109,8 @@ func (g *Generator) RunIndex() error {
109109 return g .writeIndex (groups )
110110}
111111
112+ // validateDocGroups checks that every group's output path is safe before writing begins.
113+ // @intent prevent path-traversal writes before any file I/O is attempted
112114func (g * Generator ) validateDocGroups (groups []nodeGroup ) error {
113115 for _ , grp := range groups {
114116 if _ , err := safeDocOutputPath (g .OutDir , grp .FilePath + ".md" ); err != nil {
@@ -177,6 +179,8 @@ func (g *Generator) loadEdges(nodeIDs []uint) (map[uint][]model.Edge, error) {
177179 return result , nil
178180}
179181
182+ // generatedFiles builds the list of output paths that this run will produce.
183+ // @intent track expected output files so the manifest and prune step stay consistent
180184func generatedFiles (groups []nodeGroup ) []string {
181185 files := make ([]string , 0 , len (groups )+ 1 )
182186 for _ , grp := range groups {
@@ -186,6 +190,8 @@ func generatedFiles(groups []nodeGroup) []string {
186190 return files
187191}
188192
193+ // manifestPath returns the namespace-scoped path for the docs manifest file.
194+ // @intent isolate manifest files per namespace so concurrent namespaces do not collide
189195func (g * Generator ) manifestPath () string {
190196 name := ".ccg-docs-manifest.json"
191197 if g .Namespace != "" {
@@ -194,6 +200,8 @@ func (g *Generator) manifestPath() string {
194200 return filepath .Join (g .OutDir , name )
195201}
196202
203+ // loadManifest reads the previously saved manifest from disk, returning an empty manifest if none exists.
204+ // @intent restore the prior output file list so Run can compute stale files to prune
197205func (g * Generator ) loadManifest () (* manifest , error ) {
198206 m := & manifest {Namespace : g .Namespace }
199207 data , err := os .ReadFile (g .manifestPath ())
@@ -209,6 +217,9 @@ func (g *Generator) loadManifest() (*manifest, error) {
209217 return m , nil
210218}
211219
220+ // saveManifest persists the list of generated files to disk for use by the next run's prune step.
221+ // @intent record which files were written so future runs can detect and remove stale docs
222+ // @sideEffect writes .ccg-docs-manifest[.namespace].json to g.OutDir
212223func (g * Generator ) saveManifest (files []string ) error {
213224 data , err := json .MarshalIndent (manifest {Namespace : g .Namespace , Files : files }, "" , " " )
214225 if err != nil {
@@ -220,6 +231,10 @@ func (g *Generator) saveManifest(files []string) error {
220231 return atomicWriteFile (g .manifestPath (), data , 0644 )
221232}
222233
234+ // pruneManaged deletes previously generated docs that are no longer in the current output set,
235+ // but only if the file starts with the managed marker to avoid removing user-edited files.
236+ // @intent clean up stale generated docs without touching manually created files
237+ // @sideEffect removes markdown files from g.OutDir that are no longer part of the current graph
223238func (g * Generator ) pruneManaged (previous , current []string ) error {
224239 keep := map [string ]bool {}
225240 for _ , p := range current {
0 commit comments