docs(flows): DoD obligatorio con user-facing surface + abrir issues 0100-0103 (taxonomia, frontmatter migration, dev_console, work dashboard)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 00:07:03 +02:00
parent 212875ed0d
commit 5d2a14e50a
77 changed files with 4062 additions and 311 deletions
+59 -2
View File
@@ -16,6 +16,7 @@ type IndexResult struct {
Analysis int
Projects int
Vaults int
Modules int
UnitTests int
ValidationErrors []string
Warnings []string
@@ -31,7 +32,7 @@ type IndexResult struct {
// directories (e.g. python/functions/, python/types/).
func Index(db *DB, root string) (*IndexResult, error) {
// Load existing timestamps before purging so we can preserve created_at
oldFuncs, oldTypes, oldApps, oldAnalysis, oldProjects, oldVaults, err := db.LoadTimestamps()
oldFuncs, oldTypes, oldApps, oldAnalysis, oldProjects, oldVaults, oldModules, err := db.LoadTimestamps()
if err != nil {
return nil, fmt.Errorf("loading timestamps: %w", err)
}
@@ -62,6 +63,20 @@ func Index(db *DB, root string) (*IndexResult, error) {
}
}
// Discover module directories (modules/<name>/) — each may contain function .md
// files alongside the module.md. Module entrypoint .md files (e.g. data_table.md)
// live in their module dir; types still live in types/ to keep cross-module reuse.
modRoot := filepath.Join(root, "modules")
if fi, err := os.Stat(modRoot); err == nil && fi.IsDir() {
modEntries, _ := os.ReadDir(modRoot)
for _, me := range modEntries {
if !me.IsDir() {
continue
}
funcDirs = append(funcDirs, filepath.Join(modRoot, me.Name()))
}
}
for _, dir := range funcDirs {
walkMD(dir, func(path string) {
f, err := ParseFunctionMD(path, root)
@@ -146,6 +161,31 @@ func Index(db *DB, root string) (*IndexResult, error) {
}
}
// Parse modules from modules/*/module.md
var modules []*Module
modulesDir := filepath.Join(root, "modules")
if fi, err := os.Stat(modulesDir); err == nil && fi.IsDir() {
modEntries, _ := os.ReadDir(modulesDir)
for _, me := range modEntries {
if !me.IsDir() {
continue
}
modMD := filepath.Join(modulesDir, me.Name(), "module.md")
if _, err := os.Stat(modMD); err != nil {
continue
}
m, err := ParseModuleMD(modMD, root)
if err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("parse %s: %v", modMD, err))
continue
}
if m.DirPath == "" {
m.DirPath = filepath.Join("modules", me.Name())
}
modules = append(modules, m)
}
}
// Parse projects from projects/*/project.md
var projects []*Project
var vaults []*Vault
@@ -347,6 +387,19 @@ func Index(db *DB, root string) (*IndexResult, error) {
result.Vaults++
}
for _, m := range modules {
m.ContentHash = ComputeModuleHash(m)
applyTimestamps(&m.CreatedAt, &m.UpdatedAt, m.ContentHash, oldModules[m.ID], now)
if err := db.InsertModule(m); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("insert module %s: %v", m.ID, err))
continue
}
if err := emitModuleVersionHeader(m, root); err != nil {
result.Warnings = append(result.Warnings, fmt.Sprintf("module %s: codegen version header: %v", m.ID, err))
}
result.Modules++
}
// Extract unit tests from test files of tested functions
if err := db.PurgeUnitTests(); err != nil {
result.Warnings = append(result.Warnings, fmt.Sprintf("purging unit_tests: %v", err))
@@ -437,7 +490,8 @@ func applyTimestamps(createdAt, updatedAt *time.Time, newHash string, old timest
}
}
// walkMD walks a directory recursively and calls fn for each .md file found.
// walkMD walks a directory recursively and calls fn for each .md file found,
// skipping module.md (which is parsed separately as a Module entry).
func walkMD(dir string, fn func(path string)) {
if _, err := os.Stat(dir); err != nil {
return
@@ -446,6 +500,9 @@ func walkMD(dir string, fn func(path string)) {
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".md") {
return nil
}
if filepath.Base(path) == "module.md" {
return nil
}
fn(path)
return nil
})