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
+49
View File
@@ -61,6 +61,8 @@ func cmdDoctor(args []string) {
}
case "app-location":
doctorAppLocation(r, jsonOut)
case "modules":
doctorModules(r, jsonOut)
default:
fmt.Fprintf(os.Stderr, "unknown doctor subcommand: %s\n", sub)
doctorUsage()
@@ -87,6 +89,7 @@ Subcommands:
copied-code Detecta cuerpos de funcion del registry copiados en apps sin import (issue 0085k)
capabilities Drift entre docs/capabilities/INDEX.md, tags de funciones, y paginas <grupo>.md (issue 0086)
app-location Detecta artefactos (apps/analysis) en carpetas de lenguaje (cpp/apps/, etc.) - issue 0096
modules Drift entre uses_modules (app.md) y fn_module_<x> link calls (CMakeLists.txt) - issue 0097
Flags:
--json Salida JSON (para scripting/agentes)
@@ -539,3 +542,49 @@ func doctorAppLocation(root string, jsonOut bool) {
w.Flush()
fmt.Printf("\n%d violation(s): move artefact to apps/<name>/ or projects/<p>/apps/<name>/ (issue 0096).\n", len(violations))
}
func doctorModules(root string, jsonOut bool) {
checks, err := infra.AuditModulesDrift(root)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
if jsonOut {
emit(checks)
return
}
bad := 0
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "STATUS\tAPP\tDECLARED\tLINKED\tMISSING\tEXTRA")
for _, c := range checks {
status := "OK"
if !c.OK {
status = "DRIFT"
bad++
}
decl := strings.Join(c.Declared, ",")
if decl == "" {
decl = "-"
}
link := strings.Join(c.Linked, ",")
if link == "" {
link = "-"
}
missing := strings.Join(c.MissingLinks, ",")
if missing == "" {
missing = "-"
}
extra := strings.Join(c.ExtraLinks, ",")
if extra == "" {
extra = "-"
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", status, c.AppID, decl, link, missing, extra)
}
w.Flush()
fmt.Printf("\n%d/%d apps with module drift.\n", bad, len(checks))
if bad > 0 {
fmt.Println("Fix: align uses_modules in app.md with target_link_libraries(fn_module_*) in CMakeLists.txt.")
}
}
+37 -2
View File
@@ -143,8 +143,8 @@ func cmdIndex() {
}
}
fmt.Printf("Indexed %d functions, %d types, %d apps, %d analysis, %d projects, %d vaults, %d unit_tests\n",
result.Functions, result.Types, result.Apps, result.Analysis, result.Projects, result.Vaults, result.UnitTests)
fmt.Printf("Indexed %d functions, %d types, %d apps, %d analysis, %d projects, %d vaults, %d modules, %d unit_tests\n",
result.Functions, result.Types, result.Apps, result.Analysis, result.Projects, result.Vaults, result.Modules, result.UnitTests)
for _, e := range result.ValidationErrors {
fmt.Fprintf(os.Stderr, " INVALID: %s\n", e)
}
@@ -420,10 +420,42 @@ func cmdShow(args []string) {
return
}
m, errM := db.GetModule(id)
if errM == nil {
printModule(m)
return
}
fmt.Fprintf(os.Stderr, "not found: %s\n", id)
os.Exit(1)
}
func printModule(m *registry.Module) {
fmt.Printf("ID: %s\n", m.ID)
fmt.Printf("Name: %s\n", m.Name)
fmt.Printf("Version: %s\n", m.Version)
fmt.Printf("Lang: %s\n", m.Lang)
fmt.Printf("Description: %s\n", m.Description)
if len(m.Members) > 0 {
fmt.Printf("Members: %s\n", strings.Join(m.Members, ", "))
}
if len(m.Tags) > 0 {
fmt.Printf("Tags: %s\n", strings.Join(m.Tags, ", "))
}
if m.DirPath != "" {
fmt.Printf("DirPath: %s\n", m.DirPath)
}
if m.RepoURL != "" {
fmt.Printf("RepoURL: %s\n", m.RepoURL)
}
if m.Documentation != "" {
fmt.Printf("\nDocumentation:\n%s\n", m.Documentation)
}
if m.Notes != "" {
fmt.Printf("\nNotes:\n%s\n", m.Notes)
}
}
func printFunction(f *registry.Function) {
fmt.Printf("ID: %s\n", f.ID)
fmt.Printf("Name: %s\n", f.Name)
@@ -540,6 +572,9 @@ func printApp(a *registry.App) {
if len(a.UsesTypes) > 0 {
fmt.Printf("Uses types: %s\n", strings.Join(a.UsesTypes, ", "))
}
if len(a.UsesModules) > 0 {
fmt.Printf("Uses mods: %s\n", strings.Join(a.UsesModules, ", "))
}
if a.Notes != "" {
fmt.Printf("\nNotes:\n%s\n", a.Notes)
}
+14
View File
@@ -27,6 +27,7 @@ type syncRequest struct {
Analysis []registry.Analysis `json:"analysis"`
Projects []registry.Project `json:"projects"`
Vaults []registry.Vault `json:"vaults"`
Modules []registry.Module `json:"modules"`
Proposals []registry.Proposal `json:"proposals"`
Locations []registry.PcLocation `json:"locations"`
}
@@ -37,6 +38,7 @@ type syncResponse struct {
Analysis []registry.Analysis `json:"analysis"`
Projects []registry.Project `json:"projects"`
Vaults []registry.Vault `json:"vaults"`
Modules []registry.Module `json:"modules"`
Proposals []registry.Proposal `json:"proposals"`
Locations []registry.PcLocation `json:"locations"`
Stats struct {
@@ -100,6 +102,7 @@ func syncPushPull() {
analysis, _ := db.AllAnalysis()
projects, _ := db.ListAllProjects()
vaults, _ := db.AllVaults()
modules, _ := db.AllModules()
proposals, _ := db.AllProposals()
// 2. Scan local directories and build pc_locations
@@ -112,6 +115,7 @@ func syncPushPull() {
Analysis: analysis,
Projects: projects,
Vaults: vaults,
Modules: modules,
Proposals: proposals,
Locations: locations,
}
@@ -203,6 +207,14 @@ func applySync(db *registry.DB, resp syncResponse) int {
}
}
for _, m := range resp.Modules {
existing, err := db.GetModule(m.ID)
if err != nil || m.UpdatedAt.After(existing.UpdatedAt) {
db.InsertModule(&m)
imported++
}
}
for _, p := range resp.Proposals {
existing, err := db.GetProposal(p.ID)
if err != nil || p.UpdatedAt.After(existing.UpdatedAt) {
@@ -329,6 +341,7 @@ func syncStatus() {
analysis, _ := db.AllAnalysis()
projects, _ := db.ListAllProjects()
vaults, _ := db.AllVaults()
modules, _ := db.AllModules()
proposals, _ := db.AllProposals()
locs, _ := db.ListAllPcLocations()
@@ -337,6 +350,7 @@ func syncStatus() {
fmt.Printf(" analysis: %d\n", len(analysis))
fmt.Printf(" projects: %d\n", len(projects))
fmt.Printf(" vaults: %d\n", len(vaults))
fmt.Printf(" modules: %d\n", len(modules))
fmt.Printf(" proposals: %d\n", len(proposals))
fmt.Printf(" locations: %d\n", len(locs))
+29
View File
@@ -0,0 +1,29 @@
package main
import (
"flag"
"fmt"
"os"
"fn-registry/functions/browser"
)
func main() {
port := flag.Int("port", 9222, "CDP debug port")
headless := flag.Bool("headless", false, "headless mode")
chromePath := flag.String("chrome-path", "", "explicit chrome.exe path (optional)")
userDataDir := flag.String("user-data-dir", "", "user-data-dir (optional; WSL2 auto-translates)")
flag.Parse()
pid, err := browser.ChromeLaunch(browser.ChromeLaunchOpts{
Port: *port,
Headless: *headless,
ChromePath: *chromePath,
UserDataDir: *userDataDir,
})
if err != nil {
fmt.Fprintf(os.Stderr, "chrome_launch failed: %v\n", err)
os.Exit(1)
}
fmt.Printf("OK pid=%d port=%d\n", pid, *port)
}