@antoneeo/agentic-sdlc-skill 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -25
- package/gemini-extension.json +2 -2
- package/package.json +6 -6
- package/references/architecture_template.md +11 -7
- package/references/existing_features_template.md +4 -0
- package/skills/agentic-sdlc-skill/SKILL.md +31 -0
- package/skills/agentic-sdlc-skill/scripts/sdlc_check.py +154 -7
- package/skills/agentic-sdlc-skill/templates.md +25 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,30 +1,52 @@
|
|
|
1
1
|
# Changelog - Agentic SDLC Skill
|
|
2
2
|
|
|
3
|
-
Tutte le modifiche significative a questa skill saranno documentate in questo file.
|
|
4
|
-
|
|
5
|
-
## [1.
|
|
6
|
-
### Added
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
3
|
+
Tutte le modifiche significative a questa skill saranno documentate in questo file.
|
|
4
|
+
|
|
5
|
+
## [1.6.0] - 2026-06-15
|
|
6
|
+
### Added
|
|
7
|
+
- **Manifest generato dei documenti canonici** (`ai_docs/INDEX.md`): `sdlc_check.py index` ora produce, oltre a `features_history.md`, un indice completo di tutti i doc in `vision/`, `reference/`, `architecture/`, `functional/`, `strategic/`, con descrizione e stato letti dall'header. Si rigenera, quindi non drifta.
|
|
8
|
+
- **Lifecycle dei documenti canonici**: convenzione header `status: CURRENT|SUPERSEDED|DRAFT|DEPRECATED` + `supersedes:`. `validate` avvisa se `status` manca/è invalido o se un doc superseduto è ancora `CURRENT`. Stop ai grep che riportano a guide obsolete.
|
|
9
|
+
- **Modello a due indici** documentato nella sezione "Documenti ai_docs" di `SKILL.md`: `README.md` curato (must-read, a mano) vs `INDEX.md` generato (completo, meccanico) — ruoli separati, prima confusi in un unico README che driftava.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- §1 Audit: leggere `README.md` + `INDEX.md` all'avvio per sapere cosa esiste prima di esplorare il codice.
|
|
13
|
+
- §3 Analisi: cercare con glob/grep un'ANALYSIS esistente prima di crearne una nuova (anti-duplicazione).
|
|
14
|
+
- §5 Chiusura: gate "Indici allineati" — rigenerare `INDEX.md`, aggiornare il `README.md` curato per i must-read, marcare lo `status`; doc canonico non indicizzato o senza `status` = chiusura sporca.
|
|
15
|
+
- `templates.md`: aggiunto il template dell'header dei documenti canonici.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `sdlc_check.py` legge ora i file con `utf-8-sig`: un BOM iniziale (file autorati su Windows) non impedisce più il riconoscimento del frontmatter `---`.
|
|
19
|
+
- L'estrattore dell'header riconosce sia il frontmatter `status:` sia la riga in corpo `**Status:**`/`Stato:`, e gli stati di tutte le convenzioni in uso (canonici `CURRENT/SUPERSEDED/DRAFT/DEPRECATED`, vision `DRAFT/APPROVED`, ADR `Accepted/Proposed/Rejected`) — niente più falsi avvisi "status non riconosciuto" su `APPROVED`/`Accepted`.
|
|
20
|
+
- La descrizione del manifest salta righe di metadati (`Date`, `Created`, `Task ref`, ...) e i commenti HTML, così non finiscono come descrizione del documento.
|
|
21
|
+
- `index` non genera più un `INDEX.md` vuoto su progetti senza documenti canonici (solo `solutions/`+`audit/`).
|
|
22
|
+
|
|
23
|
+
### Migrazione (da 1.5.x)
|
|
24
|
+
- Al primo `sdlc_check.py check`/`validate` dopo l'upgrade, un progetto con documenti canonici darà **un errore** `ai_docs/INDEX.md mancante`: è atteso — esegui **una volta** `sdlc_check.py index` per generarlo. Da lì in poi resta allineato.
|
|
25
|
+
- I documenti canonici preesistenti senza `status:` produrranno **avvisi** (non errori): aggiungi l'header `description:`/`status:` per silenziarli. I nuovi progetti nascono già compatibili (template aggiornati).
|
|
26
|
+
|
|
27
|
+
## [1.5.0] - 2026-06-13
|
|
28
|
+
### Added
|
|
29
|
+
- Introdotta la Regola Zero di triage (`L1`, `L2`, `L3`, `Spike`) per rendere il processo proporzionale al rischio.
|
|
30
|
+
- Aggiunta simbiosi esplicita con devPNT: in Hybrid la `M-VISION` guida la milestone, il Master Plan resta roadmap strategica e l'Action Plan governa l'esecuzione tattica.
|
|
31
|
+
- Aggiunti support file dentro la skill runtime: `templates.md`, `ENFORCEMENT.md`, `scripts/sdlc_check.py`.
|
|
32
|
+
- `agentic-sdlc-install-skill` ora installa la skill nativa anche in `~/.gemini/skills/agentic-sdlc/`.
|
|
33
|
+
- Aggiunto validatore meccanico opzionale per frontmatter ANALYSIS, Vision state, indice feature e audit stale.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Il nome pubblico resta `agentic-sdlc`; la proposta v2 e' stata integrata come evoluzione, non come skill parallela.
|
|
37
|
+
- Aggiornati `agentic-sdlc-init`, template, protocolli generati, README e metadata.
|
|
38
|
+
- La modalita Standalone resta completa; devPNT e' un livello di governance superiore, non un prerequisito.
|
|
39
|
+
|
|
40
|
+
## [1.4.0] - 2026-06-07
|
|
41
|
+
### Added
|
|
42
|
+
- Introdotta la governance della **Vision** con nuova struttura `ai_docs/vision/` (`project_vision.md`, `roadmap.md`, `principles.md`, `features/`).
|
|
43
|
+
- Aggiunto il **Vision Gate** nel workflow operativo: ogni feature significativa deve essere verificata rispetto a obiettivi, non-obiettivi, benefici attesi e segnali di successo prima dell'analisi tecnica.
|
|
44
|
+
- Aggiunti template Vision in `references/` e sezione `Allineamento alla Vision` nel template di analisi.
|
|
45
|
+
- `agentic-sdlc-init` ora crea i documenti Vision boilerplate nei nuovi progetti.
|
|
46
|
+
|
|
47
|
+
## [1.3.1] - 2026-05-14
|
|
48
|
+
### Fixed
|
|
49
|
+
- Correzione documentazione (README + CHANGELOG) della sintassi per invocare il bin `agentic-sdlc-install-skill`. La forma `npx @antoneeo/agentic-sdlc-skill agentic-sdlc-install-skill` documentata in 1.3.0 **non funziona** perché npx non riesce a disambiguare il bin quando il pacchetto ne espone più di uno (errore: `could not determine executable to run`). Sintassi corretta: lanciare `agentic-sdlc-install-skill` direttamente dopo `npm install -g`, oppure usare `npx -p @antoneeo/agentic-sdlc-skill agentic-sdlc-install-skill` con `-p` esplicito.
|
|
28
50
|
- Nessuna modifica al codice della skill: il bin di 1.3.0 funziona correttamente, era solo la doc a indicare la sintassi sbagliata.
|
|
29
51
|
|
|
30
52
|
## [1.3.0] - 2026-05-14
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-sdlc-skill",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Protocollo SDLC Documentation-First con triage, Vision governance e integrazione opzionale devPNT.",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "Protocollo SDLC Documentation-First con triage, Vision governance e integrazione opzionale devPNT.",
|
|
5
5
|
"author": "Antonio Pinto (https://github.com/Antoneeo)"
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antoneeo/agentic-sdlc-skill",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Protocollo SDLC Documentation-First per Claude Code, Gemini CLI e Codex con triage, Vision governance, support file installati e integrazione opzionale devPNT.",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "Protocollo SDLC Documentation-First per Claude Code, Gemini CLI e Codex con triage, Vision governance, support file installati e integrazione opzionale devPNT.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
7
7
|
"claude-skill",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"preuninstall": "node scripts/preuninstall.js"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"skills/agentic-sdlc-skill/SKILL.md",
|
|
29
|
-
"skills/agentic-sdlc-skill/templates.md",
|
|
30
|
-
"skills/agentic-sdlc-skill/ENFORCEMENT.md",
|
|
31
|
-
"skills/agentic-sdlc-skill/scripts/sdlc_check.py",
|
|
28
|
+
"skills/agentic-sdlc-skill/SKILL.md",
|
|
29
|
+
"skills/agentic-sdlc-skill/templates.md",
|
|
30
|
+
"skills/agentic-sdlc-skill/ENFORCEMENT.md",
|
|
31
|
+
"skills/agentic-sdlc-skill/scripts/sdlc_check.py",
|
|
32
32
|
"gemini-extension.json",
|
|
33
33
|
"references",
|
|
34
34
|
"README.md",
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Stack, struttura directory e pattern architetturali del progetto.
|
|
3
|
+
status: CURRENT
|
|
4
|
+
---
|
|
1
5
|
# Architettura del Progetto
|
|
2
6
|
|
|
3
7
|
## Stack Tecnologico
|
|
@@ -6,13 +10,13 @@
|
|
|
6
10
|
- **Database:** [es. PostgreSQL]
|
|
7
11
|
- **Strumenti di Test:** [es. Jest, Vitest]
|
|
8
12
|
|
|
9
|
-
## Struttura delle Directory
|
|
10
|
-
- `src/`: Codice sorgente.
|
|
11
|
-
- `ai_docs/vision/`: Vision di progetto, roadmap, principi e mini-vision delle feature.
|
|
12
|
-
- `ai_docs/strategic/`: Architettura, feature esistenti e storico feature.
|
|
13
|
-
- `ai_docs/solutions/`: Analisi e piani delle singole feature.
|
|
14
|
-
- `ai_docs/audit/`: Piano di audit e handoff di sessione.
|
|
15
|
-
- `tests/`: Test automatici.
|
|
13
|
+
## Struttura delle Directory
|
|
14
|
+
- `src/`: Codice sorgente.
|
|
15
|
+
- `ai_docs/vision/`: Vision di progetto, roadmap, principi e mini-vision delle feature.
|
|
16
|
+
- `ai_docs/strategic/`: Architettura, feature esistenti e storico feature.
|
|
17
|
+
- `ai_docs/solutions/`: Analisi e piani delle singole feature.
|
|
18
|
+
- `ai_docs/audit/`: Piano di audit e handoff di sessione.
|
|
19
|
+
- `tests/`: Test automatici.
|
|
16
20
|
|
|
17
21
|
## Pattern Architetturali
|
|
18
22
|
- [es. MVC, Clean Architecture, Layered Architecture]
|
|
@@ -83,6 +83,7 @@ Regole Hybrid:
|
|
|
83
83
|
### 1. Audit e Allineamento
|
|
84
84
|
|
|
85
85
|
- Leggi `ai_docs/audit/handoff.md` se esiste; se ha Data/Branch non coerenti, trattalo come storico.
|
|
86
|
+
- Leggi `ai_docs/README.md` (must-read curati) e `ai_docs/INDEX.md` (manifest generato di tutti i doc canonici) per sapere cosa esiste prima di esplorare il codice. `solutions/` e `audit/` non sono indicizzate per file: cercale con glob/grep.
|
|
86
87
|
- Se `ai_docs/` manca o e' incompleta, crea struttura e documenti minimi analizzando il progetto a lotti.
|
|
87
88
|
- In Standalone usa `ai_docs/audit/audit_plan.md` per mappatura e stato.
|
|
88
89
|
- In Hybrid preferisci la mappatura devPNT/KL quando disponibile; non duplicare la governance dei piani.
|
|
@@ -104,6 +105,7 @@ Hybrid:
|
|
|
104
105
|
### 3. Analisi della Richiesta
|
|
105
106
|
|
|
106
107
|
Standalone L3:
|
|
108
|
+
- Prima di creare un nuovo `ANALYSIS_[feature].md`, cerca con glob/grep in `ai_docs/solutions/` un'analisi gia' esistente sullo stesso tema: se c'e', aggiornala invece di duplicarla.
|
|
107
109
|
- Crea o aggiorna `ai_docs/solutions/ANALYSIS_[feature].md`.
|
|
108
110
|
- Sezioni minime: Obiettivo, Vision della Feature o Allineamento alla Vision, Impatto, Sicurezza e Threat Model, Piano d'Azione, Strategia di Test, Diario/Stato Corrente.
|
|
109
111
|
- Per feature che attraversano piu milestone o piu analisi, crea anche `ai_docs/vision/features/VISION_[feature].md`.
|
|
@@ -127,10 +129,39 @@ Hybrid L3:
|
|
|
127
129
|
- Esegui test/lint/smoke check pertinenti.
|
|
128
130
|
- Verifica allineamento con Vision locale o M-VISION devPNT.
|
|
129
131
|
- Aggiorna solo i documenti effettivamente impattati.
|
|
132
|
+
- **Indici allineati (Poka-Yoke)**: se hai creato, spostato o rimosso documenti canonici (`vision/`, `reference/`, `architecture/`, `functional/`, `strategic/`):
|
|
133
|
+
- rigenera il manifest con `sdlc_check.py index` (scrive `ai_docs/INDEX.md`) — non scriverlo a mano;
|
|
134
|
+
- se il documento e' must-read, aggiungi/aggiorna la sua riga nel `README.md` curato;
|
|
135
|
+
- se il documento ne sostituisce un altro, marca il vecchio `status: SUPERSEDED` e dichiara `supersedes:` nel nuovo;
|
|
136
|
+
- se hai creato una nuova sottodirectory canonica, dalle uno scopo nel `README.md`.
|
|
137
|
+
Doc canonico non indicizzato o senza `status` = chiusura sporca (`sdlc_check.py check` fallisce/avvisa). Non dichiarare DONE finche' non e' pulito. Dettagli: sezione "Documenti ai_docs".
|
|
130
138
|
- In Hybrid proponi ADR/KL quando ci sono decisioni architetturali.
|
|
131
139
|
- In Standalone, se il progetto adotta `sdlc_check.py`, esegui `python <skill_dir>/scripts/sdlc_check.py check --root <project_root>` o la copia locale equivalente.
|
|
132
140
|
- I documenti aggiornati devono viaggiare nello stesso commit/PR del codice che descrivono.
|
|
133
141
|
|
|
142
|
+
## Documenti ai_docs: due indici + lifecycle
|
|
143
|
+
|
|
144
|
+
I documenti in `ai_docs/` hanno due ruoli serviti da due indici distinti — non confonderli:
|
|
145
|
+
|
|
146
|
+
- **`ai_docs/README.md` (curato, a mano):** la priorita' di lettura. Poche righe, solo must-read canonici, cambia di rado. Giudizio umano su "cosa leggere prima".
|
|
147
|
+
- **`ai_docs/INDEX.md` (generato, `sdlc_check.py index`):** il manifest completo di ogni doc canonico (`vision/`, `reference/`, `architecture/`, `functional/`, `strategic/`) con descrizione e stato. Mai a mano: si rigenera, cosi' non drifta.
|
|
148
|
+
- **`strategic/features_history.md` (generato):** lo storico delle ANALYSIS, dai loro frontmatter.
|
|
149
|
+
- `audit/` e `solutions/` sono discovery-by-grep: non entrano nel manifest.
|
|
150
|
+
|
|
151
|
+
**Header dei documenti canonici (lifecycle).** Ogni doc in quelle directory dovrebbe aprirsi con un frontmatter minimo, cosi' il manifest si genera da solo e un agente sa subito se fidarsi:
|
|
152
|
+
|
|
153
|
+
```markdown
|
|
154
|
+
---
|
|
155
|
+
description: Una riga — cos'e' e quando leggerlo.
|
|
156
|
+
status: CURRENT # CURRENT | SUPERSEDED | DRAFT | DEPRECATED
|
|
157
|
+
supersedes: vecchio_doc.md # solo se rimpiazza un altro doc
|
|
158
|
+
---
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
In fallback (nessun frontmatter) il manifest deduce il titolo dal primo `# H1` e la descrizione dalla prima riga di prosa o blockquote; ma senza `status` un doc non porta segnale di freschezza. `status` mancante, valore non valido, o doc superseduto ancora `CURRENT` = avviso in `validate`. Un doc `SUPERSEDED` resta sul filesystem come storico, ma il suo stato lo dichiara morto: niente piu' grep che riportano a guide obsolete.
|
|
162
|
+
|
|
163
|
+
Senza Python/hook (ambienti minimali) gli indici e gli header restano una disciplina di prosa: aggiorna `README.md` e marca lo `status` a mano; il validatore e' solo il backstop dove e' adottato.
|
|
164
|
+
|
|
134
165
|
## Enforcement Meccanico
|
|
135
166
|
|
|
136
167
|
Il prompt non e' enforcement. Quando il progetto richiede garanzie ripetibili:
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
Comandi:
|
|
6
6
|
check gate unico di chiusura: validate + stale in un solo comando (exit 1 se uno dei due fallisce)
|
|
7
7
|
validate verifica la coerenza strutturale di ai_docs/ (exit 1 se errori)
|
|
8
|
-
index rigenera
|
|
8
|
+
index rigenera gli indici generati: strategic/features_history.md (dai frontmatter
|
|
9
|
+
delle ANALYSIS_*.md) e ai_docs/INDEX.md (manifest dei documenti canonici)
|
|
9
10
|
stale elenca le aree modificate dopo l'ultima analisi registrata in audit_plan.md (exit 1 se presenti)
|
|
10
11
|
mark registra percorsi come ANALYZED con riferimento corrente (hash git, altrimenti timestamp UTC)
|
|
11
12
|
gate hook PreToolUse: blocca scritture su percorsi protetti senza ANALYSIS IN_PROGRESS (exit 2)
|
|
@@ -28,6 +29,16 @@ SKIP_DIRS = {".git", ".hg", ".svn", "node_modules", "__pycache__", ".venv", "ven
|
|
|
28
29
|
"dist", "build", ".idea", ".vs", "ai_docs"}
|
|
29
30
|
INDEX_HEADER = ("<!-- GENERATO da sdlc_check.py index - non modificare a mano. "
|
|
30
31
|
"Fonte di verita': frontmatter dei file ANALYSIS_*.md -->")
|
|
32
|
+
MANIFEST_HEADER = ("<!-- GENERATO da sdlc_check.py index - non modificare a mano. "
|
|
33
|
+
"Fonte di verita': gli header dei documenti canonici in ai_docs/. -->")
|
|
34
|
+
# Directory i cui .md sono documenti canonici durevoli: vengono manifestati in INDEX.md.
|
|
35
|
+
# audit/ e solutions/ restano discovery-by-grep (sessione / process artifact), non manifestati.
|
|
36
|
+
MANIFEST_DIRS = ("vision", "reference", "architecture", "functional", "strategic")
|
|
37
|
+
# Stati riconosciuti: doc canonici (CURRENT/SUPERSEDED/...), vision (DRAFT/APPROVED),
|
|
38
|
+
# ADR (Accepted/Proposed/Rejected). Unione, per non dare falsi avvisi su convenzioni gia' in uso.
|
|
39
|
+
CANONICAL_STATES = {"CURRENT", "SUPERSEDED", "DRAFT", "DEPRECATED",
|
|
40
|
+
"APPROVED", "ACCEPTED", "PROPOSED", "REJECTED"}
|
|
41
|
+
GENERATED_DOCS = {"features_history.md", "INDEX.md"} # generati: mai entrate del manifest
|
|
31
42
|
MTIME_GRACE = timedelta(seconds=2)
|
|
32
43
|
|
|
33
44
|
try:
|
|
@@ -52,7 +63,9 @@ def find_project_root(start=None):
|
|
|
52
63
|
|
|
53
64
|
|
|
54
65
|
def read_text(path):
|
|
55
|
-
|
|
66
|
+
# utf-8-sig: scarta un eventuale BOM iniziale (file autorati su Windows) cosi'
|
|
67
|
+
# il frontmatter '---' a riga 0 resta riconoscibile; legge utf-8 normale altrimenti.
|
|
68
|
+
return path.read_text(encoding="utf-8-sig", errors="replace")
|
|
56
69
|
|
|
57
70
|
|
|
58
71
|
def parse_iso(value):
|
|
@@ -181,11 +194,122 @@ def build_index(root):
|
|
|
181
194
|
return "\n".join(lines) + "\n"
|
|
182
195
|
|
|
183
196
|
|
|
197
|
+
# riga "Status:"/"Stato:" nel corpo (con o senza ** **), prefisso che precede la descrizione
|
|
198
|
+
_STATUS_LINE = re.compile(r"^\**\s*(?:status|stato)\s*\**\s*:\s*\**\s*([A-Za-z][\w-]*)", re.I)
|
|
199
|
+
# righe puramente di metadati da saltare quando si sceglie la descrizione di fallback
|
|
200
|
+
_META_LINE = re.compile(r"^\**\s*(date|data|task ref|version|versione|owner|autore|branch|agente|created|creato|updated|aggiornato)\b", re.I)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def extract_doc_meta(path):
|
|
204
|
+
"""(title, description, status, supersedes) di un doc canonico.
|
|
205
|
+
|
|
206
|
+
Riconosce DUE convenzioni di header: il frontmatter YAML-lite
|
|
207
|
+
(description/status/supersedes/title) e la riga in corpo `**Status:** X`
|
|
208
|
+
(usata da ADR e doc legacy). In fallback deduce il titolo dal primo '# H1'
|
|
209
|
+
e la descrizione dalla prima riga di prosa, saltando le righe di metadati.
|
|
210
|
+
"""
|
|
211
|
+
text = read_text(path)
|
|
212
|
+
lines = text.splitlines()
|
|
213
|
+
meta = load_frontmatter(lines)
|
|
214
|
+
body = lines
|
|
215
|
+
if lines and lines[0].strip() == "---":
|
|
216
|
+
for i in range(1, min(len(lines), 60)):
|
|
217
|
+
if lines[i].strip() == "---":
|
|
218
|
+
body = lines[i + 1:]
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
title = meta.get("title", "")
|
|
222
|
+
if not title:
|
|
223
|
+
for line in body:
|
|
224
|
+
m = re.match(r"^#\s+(.*)$", line)
|
|
225
|
+
if m:
|
|
226
|
+
title = m.group(1).strip()
|
|
227
|
+
break
|
|
228
|
+
title = title or path.stem
|
|
229
|
+
|
|
230
|
+
status = meta.get("status", "").upper()
|
|
231
|
+
if not status:
|
|
232
|
+
for line in body[:25]:
|
|
233
|
+
m = _STATUS_LINE.match(line.strip())
|
|
234
|
+
if m:
|
|
235
|
+
status = m.group(1).upper()
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
desc = meta.get("description", "")
|
|
239
|
+
if not desc:
|
|
240
|
+
for line in body:
|
|
241
|
+
s = line.strip()
|
|
242
|
+
if not s or s.startswith("#") or s.startswith("<!--") or _META_LINE.match(s):
|
|
243
|
+
continue
|
|
244
|
+
if s.startswith(">"):
|
|
245
|
+
s = s.lstrip(">").strip()
|
|
246
|
+
m = _STATUS_LINE.match(s)
|
|
247
|
+
if m:
|
|
248
|
+
# "Status: X — descrizione": tieni la parte dopo lo status; se vuota, salta
|
|
249
|
+
rest = s[m.end():].strip(" *—–-:.")
|
|
250
|
+
if not rest:
|
|
251
|
+
continue
|
|
252
|
+
s = rest
|
|
253
|
+
if s:
|
|
254
|
+
desc = s
|
|
255
|
+
break
|
|
256
|
+
desc = re.sub(r"\s+", " ", desc).strip()
|
|
257
|
+
if len(desc) > 160:
|
|
258
|
+
desc = desc[:157].rstrip() + "..."
|
|
259
|
+
return title, desc, status, meta.get("supersedes", "").strip()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def list_canonical_docs(root):
|
|
263
|
+
"""[(rel_to_ai_docs, path, (title, desc, status, supersedes))] per i doc canonici."""
|
|
264
|
+
ai = root / "ai_docs"
|
|
265
|
+
out = []
|
|
266
|
+
for d in MANIFEST_DIRS:
|
|
267
|
+
base = ai / d
|
|
268
|
+
if not base.is_dir():
|
|
269
|
+
continue
|
|
270
|
+
for p in sorted(base.rglob("*.md")):
|
|
271
|
+
if p.name in GENERATED_DOCS or p.name == "README.md":
|
|
272
|
+
continue
|
|
273
|
+
out.append((p.relative_to(ai).as_posix(), p, extract_doc_meta(p)))
|
|
274
|
+
return out
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def build_manifest(root):
|
|
278
|
+
docs = list_canonical_docs(root)
|
|
279
|
+
lines = [MANIFEST_HEADER,
|
|
280
|
+
"# Indice documenti `ai_docs/` (generato)",
|
|
281
|
+
"",
|
|
282
|
+
"Manifest completo dei documenti canonici. Per la priorita' di lettura (must-read)",
|
|
283
|
+
"vedi il `README.md` curato a mano. Lo storico delle ANALYSIS e' in",
|
|
284
|
+
"`strategic/features_history.md`. `audit/` e `solutions/` sono discovery-by-grep,",
|
|
285
|
+
"non manifestate qui."]
|
|
286
|
+
by_dir = {}
|
|
287
|
+
for rel, _, meta in docs:
|
|
288
|
+
by_dir.setdefault(rel.split("/", 1)[0], []).append((rel, meta))
|
|
289
|
+
for top in MANIFEST_DIRS:
|
|
290
|
+
rows = by_dir.get(top)
|
|
291
|
+
if not rows:
|
|
292
|
+
continue
|
|
293
|
+
lines += ["", f"## {top}/", "",
|
|
294
|
+
"| Documento | Stato | Descrizione |", "|---|---|---|"]
|
|
295
|
+
for rel, (title, desc, status, _sup) in rows:
|
|
296
|
+
d = (desc or title).replace("|", "\\|")
|
|
297
|
+
lines.append(f"| `{rel}` | {status or '-'} | {d} |")
|
|
298
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
299
|
+
|
|
300
|
+
|
|
184
301
|
def cmd_index(root):
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
print(f"[ok] indice rigenerato: {
|
|
302
|
+
hist = root / "ai_docs" / "strategic" / "features_history.md"
|
|
303
|
+
hist.parent.mkdir(parents=True, exist_ok=True)
|
|
304
|
+
hist.write_text(build_index(root), encoding="utf-8")
|
|
305
|
+
print(f"[ok] indice ANALYSIS rigenerato: {hist}")
|
|
306
|
+
# INDEX.md solo se esistono doc canonici: niente manifest vuoto su progetti minimali
|
|
307
|
+
if list_canonical_docs(root):
|
|
308
|
+
manifest = root / "ai_docs" / "INDEX.md"
|
|
309
|
+
manifest.write_text(build_manifest(root), encoding="utf-8")
|
|
310
|
+
print(f"[ok] manifest documenti rigenerato: {manifest}")
|
|
311
|
+
else:
|
|
312
|
+
print("[info] nessun documento canonico: INDEX.md non generato")
|
|
189
313
|
return 0
|
|
190
314
|
|
|
191
315
|
|
|
@@ -250,6 +374,29 @@ def cmd_validate(root):
|
|
|
250
374
|
elif norm_text(read_text(hist)) != norm_text(build_index(root)):
|
|
251
375
|
errors.append("strategic/features_history.md non allineato alle ANALYSIS: esegui 'sdlc_check.py index'")
|
|
252
376
|
|
|
377
|
+
# Manifest dei documenti canonici allineato (Poka-Yoke: file non indicizzato = chiusura sporca)
|
|
378
|
+
docs = list_canonical_docs(root)
|
|
379
|
+
manifest = ai / "INDEX.md"
|
|
380
|
+
if docs:
|
|
381
|
+
if not manifest.is_file():
|
|
382
|
+
errors.append("ai_docs/INDEX.md mancante: esegui 'sdlc_check.py index'")
|
|
383
|
+
elif norm_text(read_text(manifest)) != norm_text(build_manifest(root)):
|
|
384
|
+
errors.append("ai_docs/INDEX.md non allineato ai documenti canonici: esegui 'sdlc_check.py index'")
|
|
385
|
+
|
|
386
|
+
# Lifecycle dei documenti canonici: status dichiarato + coerenza supersedes
|
|
387
|
+
canon_status = {rel: meta[2] for rel, _, meta in docs}
|
|
388
|
+
for rel, _, (title, desc, status, supersedes) in docs:
|
|
389
|
+
if not status:
|
|
390
|
+
warnings.append(f"{rel}: manca 'status:' nell'header (CURRENT/SUPERSEDED/DRAFT/DEPRECATED)")
|
|
391
|
+
elif status not in CANONICAL_STATES:
|
|
392
|
+
warnings.append(f"{rel}: status '{status}' non riconosciuto ({'/'.join(sorted(CANONICAL_STATES))})")
|
|
393
|
+
if supersedes:
|
|
394
|
+
base = os.path.basename(supersedes)
|
|
395
|
+
for other, ost in canon_status.items():
|
|
396
|
+
if (other == supersedes or other.endswith("/" + supersedes)
|
|
397
|
+
or os.path.basename(other) == base) and ost == "CURRENT":
|
|
398
|
+
warnings.append(f"{other}: ancora CURRENT ma superseduto da {rel} (impostare status: SUPERSEDED)")
|
|
399
|
+
|
|
253
400
|
# Handoff: intestazione e freschezza
|
|
254
401
|
hand = ai / "audit" / "handoff.md"
|
|
255
402
|
if hand.is_file():
|
|
@@ -443,7 +590,7 @@ def main(argv=None):
|
|
|
443
590
|
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
444
591
|
sub.add_parser("check", parents=[common], help="gate di chiusura: validate + stale in un solo comando")
|
|
445
592
|
sub.add_parser("validate", parents=[common], help="verifica coerenza di ai_docs/")
|
|
446
|
-
sub.add_parser("index", parents=[common], help="rigenera features_history.md")
|
|
593
|
+
sub.add_parser("index", parents=[common], help="rigenera features_history.md + ai_docs/INDEX.md")
|
|
447
594
|
sub.add_parser("stale", parents=[common], help="aree modificate dopo l'ultima analisi")
|
|
448
595
|
mp = sub.add_parser("mark", parents=[common], help="registra percorsi come ANALYZED")
|
|
449
596
|
mp.add_argument("paths", nargs="+", help="percorsi relativi alla radice del progetto")
|
|
@@ -5,6 +5,21 @@ Regole generali:
|
|
|
5
5
|
- La conformità al template non è l'obiettivo: se una sezione non ha contenuto reale, scrivi esplicitamente perché non si applica. Mai testo riempitivo.
|
|
6
6
|
- Date sempre assolute, in UTC dove indicato.
|
|
7
7
|
|
|
8
|
+
## Header dei documenti canonici (vision/ reference/ architecture/ functional/ strategic/)
|
|
9
|
+
|
|
10
|
+
Ogni documento canonico durevole apre con questo frontmatter: alimenta il manifest generato `ai_docs/INDEX.md` e dà a un agente il segnale di freschezza prima che si fidi del contenuto.
|
|
11
|
+
|
|
12
|
+
```markdown
|
|
13
|
+
---
|
|
14
|
+
description: Una riga — cos'è il documento e quando leggerlo.
|
|
15
|
+
status: CURRENT # CURRENT | SUPERSEDED | DRAFT | DEPRECATED
|
|
16
|
+
supersedes: vecchio_doc.md # solo se rimpiazza un altro doc canonico
|
|
17
|
+
---
|
|
18
|
+
# Titolo del Documento
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Quando un doc ne sostituisce un altro: il nuovo dichiara `supersedes:`, il vecchio passa a `status: SUPERSEDED` (resta come storico, non si cancella). `sdlc_check.py validate` avvisa se `status` manca o se un doc superseduto è ancora `CURRENT`.
|
|
22
|
+
|
|
8
23
|
## ai_docs/vision/project_vision.md
|
|
9
24
|
|
|
10
25
|
```markdown
|
|
@@ -152,9 +167,13 @@ Agente: Claude
|
|
|
152
167
|
|
|
153
168
|
## ai_docs/strategic/architecture.md e existing_features.md
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
Doc canonici: aprono con l'header (`description:`/`status:`) cosi' entrano puliti nel manifest `INDEX.md`.
|
|
156
171
|
|
|
157
172
|
```markdown
|
|
173
|
+
---
|
|
174
|
+
description: Stack, struttura directory e pattern architetturali del progetto.
|
|
175
|
+
status: CURRENT
|
|
176
|
+
---
|
|
158
177
|
# Architettura del Progetto
|
|
159
178
|
## Stack Tecnologico
|
|
160
179
|
## Struttura delle Directory
|
|
@@ -162,8 +181,12 @@ Invariati rispetto alla v1:
|
|
|
162
181
|
```
|
|
163
182
|
|
|
164
183
|
```markdown
|
|
184
|
+
---
|
|
185
|
+
description: Catalogo sintetico delle funzionalità esistenti del progetto.
|
|
186
|
+
status: CURRENT
|
|
187
|
+
---
|
|
165
188
|
# Funzionalità Esistenti
|
|
166
189
|
- [ID] **Nome Feature**: Descrizione
|
|
167
190
|
```
|
|
168
191
|
|
|
169
|
-
`ai_docs/strategic/features_history.md` NON
|
|
192
|
+
`ai_docs/strategic/features_history.md` e `ai_docs/INDEX.md` NON hanno template: sono generati da `sdlc_check.py index`.
|