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:
+133
-12
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user