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
+133 -12
View File
@@ -307,12 +307,12 @@ func (db *DB) InsertApp(a *App) error {
INSERT OR REPLACE INTO apps (
id, name, lang, domain, description, tags,
uses_functions, uses_types, framework, entry_point,
documentation, notes, dir_path, content_hash, created_at, updated_at, repo_url, project_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
documentation, notes, dir_path, content_hash, created_at, updated_at, repo_url, project_id, uses_modules
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
a.ID, a.Name, a.Lang, a.Domain, a.Description, marshalStrings(a.Tags),
marshalStrings(a.UsesFunctions), marshalStrings(a.UsesTypes), a.Framework, a.EntryPoint,
a.Documentation, a.Notes, a.DirPath, a.ContentHash, a.CreatedAt.Format(time.RFC3339), a.UpdatedAt.Format(time.RFC3339),
a.RepoURL, a.ProjectID,
a.RepoURL, a.ProjectID, marshalStrings(a.UsesModules),
)
return err
}
@@ -372,14 +372,14 @@ func scanApps(rows interface{ Next() bool; Scan(...any) error }) ([]App, error)
var result []App
for rows.Next() {
var a App
var tagsJSON, usesFnJSON, usesTypJSON string
var tagsJSON, usesFnJSON, usesTypJSON, usesModJSON string
var createdAt, updatedAt string
err := rows.Scan(
&a.ID, &a.Name, &a.Lang, &a.Domain, &a.Description, &tagsJSON,
&usesFnJSON, &usesTypJSON, &a.Framework, &a.EntryPoint,
&a.Documentation, &a.Notes, &a.DirPath, &createdAt, &updatedAt, &a.ContentHash,
&a.RepoURL, &a.ProjectID,
&a.RepoURL, &a.ProjectID, &usesModJSON,
)
if err != nil {
return nil, fmt.Errorf("scanning app: %w", err)
@@ -388,6 +388,7 @@ func scanApps(rows interface{ Next() bool; Scan(...any) error }) ([]App, error)
a.Tags = unmarshalStrings(tagsJSON)
a.UsesFunctions = unmarshalStrings(usesFnJSON)
a.UsesTypes = unmarshalStrings(usesTypJSON)
a.UsesModules = unmarshalStrings(usesModJSON)
a.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
a.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
@@ -396,7 +397,7 @@ func scanApps(rows interface{ Next() bool; Scan(...any) error }) ([]App, error)
return result, nil
}
// Purge deletes all data from functions, types, apps, analysis, projects and vaults. Used before re-indexing.
// Purge deletes all data from functions, types, apps, analysis, projects, vaults and modules. Used before re-indexing.
func (db *DB) Purge() error {
if _, err := db.conn.Exec("DELETE FROM functions"); err != nil {
return err
@@ -413,7 +414,10 @@ func (db *DB) Purge() error {
if _, err := db.conn.Exec("DELETE FROM projects"); err != nil {
return err
}
_, err := db.conn.Exec("DELETE FROM vaults")
if _, err := db.conn.Exec("DELETE FROM vaults"); err != nil {
return err
}
_, err := db.conn.Exec("DELETE FROM modules")
return err
}
@@ -458,6 +462,10 @@ func (db *DB) PurgeLocalOnly(localAppIDs, localAnalysisIDs, localProjectIDs map[
if _, err := db.conn.Exec("DELETE FROM vaults"); err != nil {
return err
}
// Modules: always purge and re-insert from modules/*/module.md
if _, err := db.conn.Exec("DELETE FROM modules"); err != nil {
return err
}
return nil
}
@@ -481,12 +489,12 @@ func (db *DB) InsertAnalysis(a *Analysis) error {
INSERT OR REPLACE INTO analysis (
id, name, lang, domain, description, tags,
uses_functions, uses_types, framework, entry_point,
documentation, notes, repo_url, dir_path, content_hash, created_at, updated_at, project_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
documentation, notes, repo_url, dir_path, content_hash, created_at, updated_at, project_id, uses_modules
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
a.ID, a.Name, a.Lang, a.Domain, a.Description, marshalStrings(a.Tags),
marshalStrings(a.UsesFunctions), marshalStrings(a.UsesTypes), a.Framework, a.EntryPoint,
a.Documentation, a.Notes, a.RepoURL, a.DirPath, a.ContentHash,
a.CreatedAt.Format(time.RFC3339), a.UpdatedAt.Format(time.RFC3339), a.ProjectID,
a.CreatedAt.Format(time.RFC3339), a.UpdatedAt.Format(time.RFC3339), a.ProjectID, marshalStrings(a.UsesModules),
)
return err
}
@@ -556,14 +564,14 @@ func scanAnalysis(rows interface{ Next() bool; Scan(...any) error }) ([]Analysis
var result []Analysis
for rows.Next() {
var a Analysis
var tagsJSON, usesFnJSON, usesTypJSON string
var tagsJSON, usesFnJSON, usesTypJSON, usesModJSON string
var createdAt, updatedAt string
err := rows.Scan(
&a.ID, &a.Name, &a.Lang, &a.Domain, &a.Description, &tagsJSON,
&usesFnJSON, &usesTypJSON, &a.Framework, &a.EntryPoint,
&a.Documentation, &a.Notes, &a.RepoURL, &a.DirPath, &a.ContentHash,
&createdAt, &updatedAt, &a.ProjectID,
&createdAt, &updatedAt, &a.ProjectID, &usesModJSON,
)
if err != nil {
return nil, fmt.Errorf("scanning analysis: %w", err)
@@ -572,6 +580,7 @@ func scanAnalysis(rows interface{ Next() bool; Scan(...any) error }) ([]Analysis
a.Tags = unmarshalStrings(tagsJSON)
a.UsesFunctions = unmarshalStrings(usesFnJSON)
a.UsesTypes = unmarshalStrings(usesTypJSON)
a.UsesModules = unmarshalStrings(usesModJSON)
a.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
a.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
@@ -1223,3 +1232,115 @@ func (db *DB) AllProposals() ([]Proposal, error) {
func (db *DB) AllVaults() ([]Vault, error) {
return db.SearchVaults("", "")
}
// --- Module CRUD ---
// InsertModule inserts or replaces a module entry.
func (db *DB) InsertModule(m *Module) error {
now := time.Now().UTC()
if m.CreatedAt.IsZero() {
m.CreatedAt = now
}
if m.UpdatedAt.IsZero() {
m.UpdatedAt = now
}
if m.ID == "" {
m.ID = GenerateModuleID(m.Name, m.Lang)
}
_, err := db.conn.Exec(`
INSERT OR REPLACE INTO modules (
id, name, version, lang, description, members, tags,
dir_path, repo_url, documentation, notes, content_hash, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
m.ID, m.Name, m.Version, m.Lang, m.Description,
marshalStrings(m.Members), marshalStrings(m.Tags),
m.DirPath, m.RepoURL, m.Documentation, m.Notes, m.ContentHash,
m.CreatedAt.Format(time.RFC3339), m.UpdatedAt.Format(time.RFC3339),
)
return err
}
// GetModule returns a single module by ID.
func (db *DB) GetModule(id string) (*Module, error) {
rows, err := db.conn.Query("SELECT * FROM modules WHERE id = ?", id)
if err != nil {
return nil, err
}
defer rows.Close()
items, err := scanModules(rows)
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, fmt.Errorf("module %q not found", id)
}
return &items[0], nil
}
// SearchModules performs FTS search on modules with optional filters.
func (db *DB) SearchModules(query, lang string) ([]Module, error) {
where := []string{}
args := []any{}
if query != "" {
where = append(where, "m.id IN (SELECT id FROM modules_fts WHERE modules_fts MATCH ?)")
args = append(args, query)
}
if lang != "" {
where = append(where, "m.lang = ?")
args = append(args, lang)
}
sql := "SELECT * FROM modules m"
if len(where) > 0 {
sql += " WHERE " + strings.Join(where, " AND ")
}
sql += " ORDER BY m.name"
rows, err := db.conn.Query(sql, args...)
if err != nil {
return nil, fmt.Errorf("search modules: %w", err)
}
defer rows.Close()
return scanModules(rows)
}
// ListAllModules returns all module entries.
func (db *DB) ListAllModules() ([]Module, error) {
return db.SearchModules("", "")
}
// AllModules returns all modules (for sync export).
func (db *DB) AllModules() ([]Module, error) {
return db.SearchModules("", "")
}
func scanModules(rows interface{ Next() bool; Scan(...any) error }) ([]Module, error) {
var result []Module
for rows.Next() {
var m Module
var membersJSON, tagsJSON string
var createdAt, updatedAt string
err := rows.Scan(
&m.ID, &m.Name, &m.Version, &m.Lang, &m.Description,
&membersJSON, &tagsJSON,
&m.DirPath, &m.RepoURL, &m.Documentation, &m.Notes, &m.ContentHash,
&createdAt, &updatedAt,
)
if err != nil {
return nil, fmt.Errorf("scanning module: %w", err)
}
m.Members = unmarshalStrings(membersJSON)
m.Tags = unmarshalStrings(tagsJSON)
m.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
m.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
result = append(result, m)
}
return result, nil
}