@antoneeo/agentic-sdlc-skill 1.7.0 → 1.8.1

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 CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  Tutte le modifiche significative a questa skill saranno documentate in questo file.
4
4
 
5
+ ## [1.8.1] - 2026-07-02
6
+ ### Fixed
7
+ - **Guide freshness hash is now line-ending independent**: `sha256_file` in `sdlc_check.py` normalizes CRLF → LF before hashing. Previously the raw-byte hash made a fresh Windows checkout with `core.autocrlf=true` rewrite `.sources/` snapshots and flag every guide `[stale]` (false positive). Backward compatible: recorded hashes were computed on LF content, and normalization maps CRLF copies back to the same digest. (Edge case: a hash recorded pre-1.8.1 on a snapshot that genuinely contained CRLF bytes will flag `[stale]` once — regenerate the hash.)
8
+ - `guides.md` step 3 now states the hash is computed over LF-normalized content and recommends the `ai_docs/reference/.sources/** -text` `.gitattributes` rule to consumer projects (defense in depth: keeps snapshots byte-verbatim).
9
+
10
+ ### Test battery addition
11
+ - Scenario 10 (extends the unit-1 battery): snapshot checked out with CRLF endings + guide recording the LF-normalized hash → `stale` must NOT flag it; a genuine content edit must still flag `[stale]`.
12
+
13
+ ## [1.8.0] - 2026-07-02 (Feature B unit 1: operative guides, project scope)
14
+ ### Added
15
+ - **Operative guides** (`ai_docs/reference/GUIDE_[topic].md`): a durable, source-faithful layer distilled from USER-PROVIDED indications — the capability neither agentic-sdlc nor superpowers had. New support file `guides.md` (pipeline: topic decomposition → user confirmation → verbatim snapshot in `reference/.sources/` with SHA-256 → source-anchored extraction → per-section fidelity markers `[source: …]` / `[not covered by source]`); guide template with provenance frontmatter (`source`, `source_version`, `distilled_from`, `source_hash`) in `templates.md`; short "Operative Guides" section in `SKILL.md`.
16
+ - **Generated guide router** `ai_docs/reference/INDEX.md` (emitted by `sdlc_check.py index`, alignment-checked by `validate`): path, status, when-to-consult line and provenance summary per guide — the pointer target for the devPNT Hybrid bridge.
17
+ - **Mechanical fidelity controls** in `sdlc_check.py`: provenance-key and per-section marker checks (warn; fails CI under `--strict`); `distilled_from` path confinement — absolute paths, `..` and symlink escapes rejected, fail-closed (ERROR); guide freshness in `stale` — recorded `source_hash` vs current snapshot, flagged in EVERY mode including `--hybrid` (guides are filesystem-first even in Hybrid).
18
+
19
+ ### Changed
20
+ - `stale --hybrid` no longer returns unconditionally 0: it still skips audit-plan staleness (delegated to devPNT/KL) but now checks guide-source drift.
21
+ - `list_canonical_docs` skips dot-subdirectories of canonical dirs (e.g. `reference/.sources/`): snapshot files are no longer swept into the manifest. Projects that kept `.md` files under dot-subdirs of canonical dirs will see them leave `INDEX.md` (more correct).
22
+
23
+ ### Process note
24
+ - First live run of **model-per-dispatch**: implementation dispatched to an economy-tier subagent working from the accepted E-TDD shadow as a self-contained brief; independent deep code review passed first round with zero blocking findings. Governance: M-VISION → D-UC → P-TM → E-ISP → E-TDD, all through independent review gates (devPNT Hybrid).
25
+
5
26
  ## [1.7.0] - 2026-07-02 (Phases 0-1 of the evolution roadmap)
6
27
  ### Changed (breaking-soft)
7
28
  - **English is now the canonical language of the skill**: `SKILL.md`, `templates.md`, `ENFORCEMENT.md`, validator messages, generated indexes and the project protocol are in English. New ANALYSIS documents use English frontmatter keys (`status`, `level`, `start_date`, `end_date`) and English section headings. **Existing projects keep working**: the validator silently accepts the deprecated Italian keys (`stato`, `livello`, `data_inizio`, `data_fine`) and Italian headings.
package/README.md CHANGED
@@ -1,81 +1,82 @@
1
- # Agentic SDLC Skill for Claude Code, Gemini CLI & Codex
2
-
3
- `agentic-sdlc` is a Documentation-First SDLC protocol for AI coding agents. It supports Claude Code, Codex, Gemini CLI, Cursor/Windsurf-style project instructions, and optional devPNT governance.
4
-
5
- ## Key Features
6
-
7
- - **Risk-proportional workflow**: L1/L2/L3/Spike triage avoids heavyweight process for trivial work.
8
- - **Vision-guided governance**: Standalone projects use `ai_docs/vision/`; Hybrid projects use devPNT `M-VISION` as the milestone north star.
9
- - **Standalone complete**: works fully with local `ai_docs/` without requiring devPNT.
10
- - **devPNT symbiosis**: when devPNT is available, Master Plan, Action Plan, M-VISION, and governed artifacts become the authoritative planning layer.
11
- - **Installed support files**: Claude, Codex, and Gemini receive the full skill folder, including `templates.md`, `ENFORCEMENT.md`, and `scripts/sdlc_check.py`.
12
- - **Mechanical checks**: optional validator for document structure, generated feature history, stale audit areas, and protected-path gates.
13
-
14
- ## Installation
15
-
16
- ### Via npm
17
-
18
- ```bash
19
- npm install -g @antoneeo/agentic-sdlc-skill@latest
20
- agentic-sdlc-install-skill
21
- ```
22
-
23
- The installer copies `skills/agentic-sdlc-skill/` recursively into native skill locations:
24
-
25
- - Claude Code: `~/.claude/skills/agentic-sdlc/`
26
- - Codex: `~/.codex/skills/agentic-sdlc/`
27
- - Gemini CLI: `~/.gemini/skills/agentic-sdlc/`
28
-
29
- Restart the relevant agent, or reload skills where the CLI supports it.
30
-
31
- The global package also exposes:
32
-
33
- ```bash
34
- agentic-sdlc-init
35
- ```
36
-
37
- Run it inside a project to create `ai_docs/`, Vision documents, strategic docs, audit plan, and agent protocol files (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`).
38
-
39
- ## Runtime Shape
40
-
41
- The actual runtime skill is the folder:
42
-
43
- ```text
44
- skills/agentic-sdlc-skill/
45
- ├── SKILL.md
46
- ├── templates.md
47
- ├── ENFORCEMENT.md
48
- └── scripts/
49
- └── sdlc_check.py
50
- ```
51
-
52
- `SKILL.md` is the entrypoint. Supporting files are loaded or executed only when the agent needs them.
53
-
54
- ## Standalone vs Hybrid
55
-
56
- Standalone:
57
-
58
- - `ai_docs/` is the source of truth.
59
- - Vision, analysis, audit, handoff, test strategy, and feature history are maintained locally.
60
-
61
- Hybrid/devPNT:
62
-
63
- - devPNT governs `M-VISION`, Master Plan, Action Plan, and versioned artifacts.
64
- - `ai_docs/` remains useful as readable context, fallback, handoff, or shadow copy.
65
- - Divergence between user request, local Vision, and devPNT `M-VISION` must be surfaced before implementation.
66
-
67
- ## Gemini Extension Alternative
68
-
69
- You can still install this folder as a Gemini extension:
70
-
71
- ```bash
72
- gemini extensions install .
73
- ```
74
-
75
- For native Gemini Agent Skills, the npm installer now copies the skill folder into `~/.gemini/skills/agentic-sdlc/`.
76
-
77
- ## Created By
78
-
79
- Created by **Antonio Pinto** ([GitHub](https://github.com/Antoneeo)).
80
-
81
- (c) 2026 Antonio Pinto. All rights reserved.
1
+ # Agentic SDLC Skill for Claude Code, Gemini CLI & Codex
2
+
3
+ `agentic-sdlc` is a Documentation-First SDLC protocol for AI coding agents. It supports Claude Code, Codex, Gemini CLI, Cursor/Windsurf-style project instructions, and optional devPNT governance.
4
+
5
+ ## Key Features
6
+
7
+ - **Risk-proportional workflow**: L1/L2/L3/Spike triage avoids heavyweight process for trivial work.
8
+ - **Vision-guided governance**: Standalone projects use `ai_docs/vision/`; Hybrid projects use devPNT `M-VISION` as the milestone north star.
9
+ - **Standalone complete**: works fully with local `ai_docs/` without requiring devPNT.
10
+ - **devPNT symbiosis**: when devPNT is available, Master Plan, Action Plan, M-VISION, and governed artifacts become the authoritative planning layer.
11
+ - **Installed support files**: Claude, Codex, and Gemini receive the full skill folder, including `templates.md`, `guides.md`, `ENFORCEMENT.md`, and `scripts/sdlc_check.py`.
12
+ - **Mechanical checks**: optional validator for document structure, generated feature history, stale audit areas, and protected-path gates.
13
+
14
+ ## Installation
15
+
16
+ ### Via npm
17
+
18
+ ```bash
19
+ npm install -g @antoneeo/agentic-sdlc-skill@latest
20
+ agentic-sdlc-install-skill
21
+ ```
22
+
23
+ The installer copies `skills/agentic-sdlc-skill/` recursively into native skill locations:
24
+
25
+ - Claude Code: `~/.claude/skills/agentic-sdlc/`
26
+ - Codex: `~/.codex/skills/agentic-sdlc/`
27
+ - Gemini CLI: `~/.gemini/skills/agentic-sdlc/`
28
+
29
+ Restart the relevant agent, or reload skills where the CLI supports it.
30
+
31
+ The global package also exposes:
32
+
33
+ ```bash
34
+ agentic-sdlc-init
35
+ ```
36
+
37
+ Run it inside a project to create `ai_docs/`, Vision documents, strategic docs, audit plan, and agent protocol files (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`).
38
+
39
+ ## Runtime Shape
40
+
41
+ The actual runtime skill is the folder:
42
+
43
+ ```text
44
+ skills/agentic-sdlc-skill/
45
+ ├── SKILL.md
46
+ ├── templates.md
47
+ ├── guides.md
48
+ ├── ENFORCEMENT.md
49
+ └── scripts/
50
+ └── sdlc_check.py
51
+ ```
52
+
53
+ `SKILL.md` is the entrypoint. Supporting files are loaded or executed only when the agent needs them.
54
+
55
+ ## Standalone vs Hybrid
56
+
57
+ Standalone:
58
+
59
+ - `ai_docs/` is the source of truth.
60
+ - Vision, analysis, audit, handoff, test strategy, and feature history are maintained locally.
61
+
62
+ Hybrid/devPNT:
63
+
64
+ - devPNT governs `M-VISION`, Master Plan, Action Plan, and versioned artifacts.
65
+ - `ai_docs/` remains useful as readable context, fallback, handoff, or shadow copy.
66
+ - Divergence between user request, local Vision, and devPNT `M-VISION` must be surfaced before implementation.
67
+
68
+ ## Gemini Extension Alternative
69
+
70
+ You can still install this folder as a Gemini extension:
71
+
72
+ ```bash
73
+ gemini extensions install .
74
+ ```
75
+
76
+ For native Gemini Agent Skills, the npm installer now copies the skill folder into `~/.gemini/skills/agentic-sdlc/`.
77
+
78
+ ## Created By
79
+
80
+ Created by **Antonio Pinto** ([GitHub](https://github.com/Antoneeo)).
81
+
82
+ (c) 2026 Antonio Pinto. All rights reserved.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-sdlc-skill",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Documentation-First SDLC protocol with triage, Vision governance and optional devPNT integration.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antoneeo/agentic-sdlc-skill",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Documentation-First SDLC protocol for Claude Code, Gemini CLI and Codex with risk triage, Vision governance, installed support files and optional devPNT integration.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -27,6 +27,7 @@
27
27
  "files": [
28
28
  "skills/agentic-sdlc-skill/SKILL.md",
29
29
  "skills/agentic-sdlc-skill/templates.md",
30
+ "skills/agentic-sdlc-skill/guides.md",
30
31
  "skills/agentic-sdlc-skill/ENFORCEMENT.md",
31
32
  "skills/agentic-sdlc-skill/scripts/sdlc_check.py",
32
33
  "gemini-extension.json",
@@ -11,6 +11,7 @@ This skill guides software development with a Documentation-First process propor
11
11
 
12
12
  Support files in the skill directory:
13
13
  - `templates.md`: templates for Vision, ANALYSIS, Spike, audit plan and handoff.
14
+ - `guides.md`: pipeline for distilling user-provided indications into `ai_docs/reference/GUIDE_[topic].md`.
14
15
  - `scripts/sdlc_check.py`: mechanical validator for `ai_docs/` (`check`, `validate`, `index`, `stale`, `mark`, `gate`).
15
16
  - `ENFORCEMENT.md`: optional setup for CI and hooks.
16
17
 
@@ -144,9 +145,10 @@ IN_PROGRESS, or vice versa. They move in the same closure step.
144
145
 
145
146
  Pass `--hybrid` explicitly (never auto-detected — an explicit flag beats a guessed
146
147
  mode): `check --hybrid` and `stale --hybrid` skip audit-plan staleness (mapping is
147
- delegated to devPNT/KL); `gate --hybrid` also unlocks on the presence of an E-TDD
148
- shadow in `solutions/` (the Hybrid design gate) instead of requiring an IN_PROGRESS
149
- ANALYSIS.
148
+ delegated to devPNT/KL) guide-drift checking still runs (`ai_docs/reference/`
149
+ is filesystem-first even in Hybrid, see the ownership matrix above); `gate --hybrid`
150
+ also unlocks on the presence of an E-TDD shadow in `solutions/` (the Hybrid design
151
+ gate) instead of requiring an IN_PROGRESS ANALYSIS.
150
152
 
151
153
  ## L3 Workflow
152
154
 
@@ -205,6 +207,7 @@ Hybrid L3:
205
207
  - if the document replaces another, mark the old one `status: SUPERSEDED` and declare `supersedes:` in the new one;
206
208
  - if you created a new canonical subdirectory, give it a purpose in `README.md`.
207
209
  A canonical doc that is unindexed or lacks `status` = dirty closure (`sdlc_check.py check` fails/warns). Do not declare DONE until it is clean. Details: section "ai_docs documents".
210
+ - guides created or changed: `sdlc_check.py index` regenerates BOTH manifests (`ai_docs/INDEX.md` and the guide router `ai_docs/reference/INDEX.md`) in one run.
208
211
  - In Hybrid propose ADR/KL updates when there were architectural decisions.
209
212
  - In Standalone, if the project adopts `sdlc_check.py`, run `python <skill_dir>/scripts/sdlc_check.py check --root <project_root>` or the equivalent local copy.
210
213
  - Updated documents must travel in the same commit/PR as the code they describe.
@@ -234,6 +237,22 @@ Without Python/hooks (minimal environments) the indexes and headers remain a pro
234
237
 
235
238
  Legacy note: the validator also accepts the deprecated Italian frontmatter keys (`stato`, `livello`, `data_inizio`, `data_fine`) and Italian section headings in existing projects. New documents must use the English forms.
236
239
 
240
+ ## Operative Guides
241
+
242
+ Trigger test: the user hands over indications to follow (origin = user, not model
243
+ knowledge) meant to govern how the agent operates (purpose = operative), not just
244
+ inform an answer. Both hold → distill into `ai_docs/reference/GUIDE_[topic].md`.
245
+
246
+ A guide TRAINS the agent, two levels: the guide is the **synthesis** a trained
247
+ agent carries (compact — read whole before acting); the verbatim snapshot in
248
+ `.sources/` is the **book**, reached on demand via the section markers. Fidelity
249
+ constraint: only what the source supports; gaps marked `[not covered by source]`,
250
+ never filled from general knowledge; selection and compression expected, addition
251
+ forbidden. Full pipeline, DRY rule, snapshotting and maintenance: `guides.md`.
252
+
253
+ `ai_docs/reference/INDEX.md` is generated (the guide router) — never edit by hand,
254
+ regenerate with `sdlc_check.py index`.
255
+
237
256
  ## Mechanical Enforcement
238
257
 
239
258
  The prompt is not enforcement. When the project needs repeatable guarantees:
@@ -0,0 +1,160 @@
1
+ # Operative Guides — pipeline
2
+
3
+ Support file for `ai_docs/reference/GUIDE_[topic].md`. Read this only when the
4
+ trigger below fires; the template lives in `templates.md`.
5
+
6
+ **The training model (what a guide IS).** A guide TRAINS the agent on a topic
7
+ the user cares about, the way training works for a person: you study the
8
+ material once, you carry a SYNTHESIS in your head, and you go back to the book
9
+ when a task needs detail. Two levels, both produced by this pipeline:
10
+ - **The guide = the synthesis** — compact and dense: core principles, decision
11
+ rules, the map of what exists, where people go wrong. Small enough that a
12
+ reader takes it in WHOLE before acting (that is the "preparation").
13
+ - **The snapshot = the book** (`.sources/`, verbatim) — the details live here,
14
+ reachable on demand.
15
+ - **The fidelity markers are the bridge**: `[source: <snapshot>#anchor]` both
16
+ proves provenance AND tells the reader where in the book the full detail is.
17
+ A guide that restates the source at length is as wrong as a fragmented one:
18
+ completeness is guaranteed by the book level, economy by the synthesis level.
19
+
20
+ ## 1. When to trigger
21
+
22
+ Trigger test is origin + purpose, not content taxonomy (no "is this technical
23
+ enough" judgement call):
24
+ - **Origin**: the user hands you indications to follow — a document to adhere
25
+ to, a pasted policy, "do it this way", a style guide, a runbook.
26
+ - **Purpose**: the material is meant to GOVERN how the agent operates, not
27
+ just inform a one-off answer.
28
+
29
+ Both hold → this is guide material. Either is missing (e.g. the user pastes
30
+ background context with no operative intent, or asks a one-off question) →
31
+ this is not a guide; answer normally, do not create a file.
32
+
33
+ Never manufacture a guide from model knowledge. If the user asks for "a guide
34
+ on X" without handing over source material, ask for the source first — a
35
+ guide with no `distilled_from` is not this pipeline's output.
36
+
37
+ ## 2. Pipeline
38
+
39
+ 0. **Search before creating (DRY — one CURRENT guide per topic).** Before
40
+ proposing anything, read `ai_docs/reference/INDEX.md` and grep
41
+ `reference/GUIDE_*.md` for topic overlap with the new material. Never end
42
+ up with two CURRENT guides on the same topic. On overlap, pick by
43
+ provenance:
44
+ - **Same source, evolved** → UPDATE the existing guide in place: new
45
+ snapshot, new `source_hash`, same file (history lives in git).
46
+ - **Different source replacing the old one** → NEW guide + mark the old
47
+ one `status: SUPERSEDED` (its provenance chain must stay honest — do not
48
+ graft a new source onto a guide distilled from another).
49
+ - **Different source, partial overlap** → flag it to the user explicitly:
50
+ the current frontmatter binds ONE source per guide (`distilled_from`/
51
+ `source_hash` are singular), so a clean multi-source merge is not yet
52
+ supported — regenerate from the prevailing source and mark what the
53
+ merge drops, or keep the topics separate if they truly are.
54
+ Semantic overlap is NOT mechanically detectable: this step is agent
55
+ discipline plus the human reviewing the router — say what you found.
56
+ **Also verify the handed SOURCE itself is current**: check its lifecycle
57
+ (status/supersedes headers) and search the project for a newer version of
58
+ the same document before snapshotting. A user may hand you a path that a
59
+ migrated copy has since superseded — distilling from it produces a guide
60
+ that is born stale. If you find a newer version, surface it and distill
61
+ from that one. (Learned the hard way on first field use, 2026-07-02.)
62
+ 1. **Decompose into PREPARATION units — and weigh the fragmentation risk.**
63
+ A guide's goal is to PREPARE an agent for a situation: everything that
64
+ situation needs must arrive in ONE guide. The risk is asymmetric — extra
65
+ context injected costs tokens (cheap, recoverable); missing context makes
66
+ the agent invent or fail (the exact failure this pipeline exists to
67
+ prevent). Every split is a bet that no future task will cross the cut.
68
+ Therefore:
69
+ - **Default = one guide per source/domain.** Split ONLY when the resulting
70
+ guides would be consulted in DISJOINT situations — no plausible task
71
+ needs two of them at once.
72
+ - **Run the split test per proposed fragment and DECLARE it** in the
73
+ proposal: "which tasks consult this fragment, and would any of those
74
+ tasks also need another fragment?" Any overlap → merge, do not split.
75
+ - Heterogeneous sources (unrelated policies handed over together) are the
76
+ legitimate split case; a single coherent document about one subsystem
77
+ almost never is.
78
+ 2. **User confirms** the topic decomposition — including the declared
79
+ fragmentation-risk assessment — before any file is written.
80
+ 3. **Snapshot each source verbatim** into
81
+ `ai_docs/reference/.sources/<slug>-<hash8>.md`:
82
+ - `slug` derives from the topic (lowercase, hyphenated).
83
+ - `hash8` = first 8 hex chars of the snapshot file's own SHA-256 (compute
84
+ the snapshot first, hash it, then name it — the hash is of the file you
85
+ just wrote, not of the original source).
86
+ - The hash is computed over LF-normalized content (CRLF → LF), exactly as
87
+ `sdlc_check.py` does in `sha256_file` — so recorded hashes survive
88
+ checkouts that rewrite line endings (e.g. Windows `core.autocrlf=true`).
89
+ - Recommended for consumer projects: add
90
+ `ai_docs/reference/.sources/** -text` to `.gitattributes` so git never
91
+ rewrites snapshot bytes at all (keeps snapshots byte-verbatim; the
92
+ normalized hash stays stable either way).
93
+ - The snapshot is verbatim: no paraphrasing, no reformatting beyond what is
94
+ needed to save it as markdown.
95
+ 4. **Source-anchored SYNTHESIS (not restatement).** Select and compress what
96
+ the source says into the operative essence — decision rules, invariants,
97
+ the "where people go wrong" list — and POINT INTO the snapshot for the
98
+ detail (`[source: <snapshot>#anchor]` doubles as the detail-lookup
99
+ pointer: "full checklist → snapshot §7"). Every claim must still trace to
100
+ a specific point in the snapshot; do not extract from memory of the
101
+ conversation — re-read the snapshot while writing each section. Selection
102
+ and compression are allowed and expected; ADDITION is not (that stays
103
+ summarize-and-expand, forbidden). A guide approaching the source's own
104
+ length is a paraphrase, not a synthesis — wrong output.
105
+ 5. **Render per template** (`templates.md` → `## ai_docs/reference/GUIDE_[topic].md`):
106
+ frontmatter with `source`, `distilled_from`, `source_hash` (the snapshot's
107
+ SHA-256, matching what you just computed), optional `source_version`; body
108
+ sections chosen from the repertoire, each with a fidelity marker.
109
+ **Write for the two-level read**: the guide (synthesis) is small enough to
110
+ be read WHOLE before acting; the snapshot (book) is where size lives and
111
+ where readers grep/partial-read on demand, following the section markers.
112
+ Use the repertoire's stable, self-describing headings, keep one concern per
113
+ `##` section, and make the frontmatter `description` the "should I open
114
+ this file at all" answer. This is why one synthesis + one book beats
115
+ several fragments: the synthesis guarantees the whole picture, the book
116
+ guarantees the details, the markers connect them.
117
+ 6. **Run `sdlc_check.py index`** so both `ai_docs/INDEX.md` and
118
+ `ai_docs/reference/INDEX.md` (the guide router) regenerate.
119
+
120
+ ## 3. Fidelity rules (mandatory, the D5 constraint)
121
+
122
+ - Only what the source supports goes in the guide. If the source is silent on
123
+ something a reader might expect, mark the section `[not covered by source]`
124
+ — never fill the gap from general knowledge.
125
+ - Every `##` section body carries exactly one kind of marker: `[source:
126
+ <snapshot>#<anchor-or-line>]` for content traceable to the snapshot, or the
127
+ literal `[not covered by source]` for an acknowledged gap. A section with
128
+ neither is a validator warning.
129
+ - `summarize-and-expand` is forbidden: do not take a short source note and
130
+ "helpfully" expand it into a longer procedure using inferred steps. If the
131
+ source says one sentence, the guide section says that one sentence
132
+ (source-anchored), not an elaborated version of it.
133
+ - When in doubt about whether something is "supported" by the source, treat it
134
+ as not covered rather than stretching the marker to fit.
135
+
136
+ ## 4. Ingestion bound (T9)
137
+
138
+ If a source document exceeds roughly 2000 lines, do not silently truncate or
139
+ skim it. Stop and ask the user to either split it into smaller documents or
140
+ select the specific sections relevant to the guide being built. Silent
141
+ truncation produces a guide that looks complete but is missing unreviewed
142
+ material — worse than asking.
143
+
144
+ ## 5. Review
145
+
146
+ Before the guide is used operatively for the first time, recommend an
147
+ independent guide-vs-source review (a fresh pass comparing the rendered guide
148
+ against the snapshot, checking every marker) — process control per the
149
+ threat model (P-TM). This is a recommendation to the user, not a hard gate:
150
+ state it explicitly when handing off a newly created guide.
151
+
152
+ ## 6. Maintenance
153
+
154
+ - **Source changed**: create a new snapshot (new hash), regenerate the guide
155
+ from it (new `source_hash`), and if the guide is replacing a prior guide
156
+ rather than updating in place, mark the old one `status: SUPERSEDED`.
157
+ - **`stale` flags hash drift**: `sdlc_check.py stale` (also under `--hybrid`)
158
+ compares each guide's recorded `source_hash` against the live snapshot file
159
+ and reports `[stale]` when they diverge — that is the signal to regenerate,
160
+ not a manual freshness check.
@@ -23,6 +23,7 @@ but are deprecated: new documents should use the English forms.
23
23
  Standard library only (Python >= 3.8). Windows and POSIX compatible.
24
24
  """
25
25
  import argparse
26
+ import hashlib
26
27
  import json
27
28
  import os
28
29
  import re
@@ -49,6 +50,11 @@ CANONICAL_STATES = {"CURRENT", "SUPERSEDED", "DRAFT", "DEPRECATED",
49
50
  "APPROVED", "ACCEPTED", "PROPOSED", "REJECTED"}
50
51
  GENERATED_DOCS = {"features_history.md", "INDEX.md"} # generated: never manifest entries
51
52
  MTIME_GRACE = timedelta(seconds=2)
53
+ GUIDE_INDEX_HEADER = ("<!-- GENERATED by sdlc_check.py index - do not edit by hand. "
54
+ "Source of truth: the headers of the GUIDE_*.md files in ai_docs/reference/. -->")
55
+ GUIDE_PROVENANCE_KEYS = ("source", "distilled_from", "source_hash") # source_version optional
56
+ # a guide section is "covered" when it carries a source marker or an explicit gap marker
57
+ GUIDE_MARKER_RE = re.compile(r"\[(?:source:[^\]]+|not covered by source)\]")
52
58
 
53
59
  # Deprecated Italian frontmatter keys, mapped to the canonical English ones.
54
60
  LEGACY_KEYS = {"stato": "status", "livello": "level",
@@ -102,6 +108,16 @@ def read_text(path):
102
108
  return path.read_text(encoding="utf-8-sig", errors="replace")
103
109
 
104
110
 
111
+ def sha256_file(path):
112
+ # CRLF->LF before hashing: a Windows checkout with core.autocrlf=true
113
+ # rewrites snapshot files, and a raw-byte hash would flag every guide
114
+ # [stale] on a fresh clone. Recorded hashes are LF-based, so normalizing
115
+ # maps CRLF copies back to the same digest.
116
+ h = hashlib.sha256()
117
+ h.update(path.read_bytes().replace(b"\r\n", b"\n"))
118
+ return h.hexdigest()
119
+
120
+
105
121
  def parse_iso(value):
106
122
  if not value:
107
123
  return None
@@ -325,6 +341,9 @@ def list_canonical_docs(root):
325
341
  if not base.is_dir():
326
342
  continue
327
343
  for p in sorted(base.rglob("*.md")):
344
+ rel_parts = p.relative_to(base).parts
345
+ if any(part.startswith(".") for part in rel_parts[:-1]):
346
+ continue # dot-subdirs (e.g. reference/.sources/) are never canonical
328
347
  if p.name in GENERATED_DOCS or p.name == "README.md":
329
348
  continue
330
349
  out.append((p.relative_to(ai).as_posix(), p, extract_doc_meta(p)))
@@ -355,6 +374,37 @@ def build_manifest(root):
355
374
  return "\n".join(lines).rstrip() + "\n"
356
375
 
357
376
 
377
+ def list_guides(root):
378
+ """[(rel_to_ai_docs, path, meta, text)] for ai_docs/reference/GUIDE_*.md."""
379
+ ref = root / "ai_docs" / "reference"
380
+ out = []
381
+ if not ref.is_dir():
382
+ return out
383
+ for p in sorted(ref.glob("GUIDE_*.md")):
384
+ text = read_text(p)
385
+ out.append((p.relative_to(root / "ai_docs").as_posix(), p,
386
+ load_frontmatter(text.splitlines()), text))
387
+ return out
388
+
389
+
390
+ def build_guide_index(root):
391
+ lines = [GUIDE_INDEX_HEADER,
392
+ "# Operative guides (generated router)",
393
+ "",
394
+ "One row per guide. `description` is the when-to-consult line; provenance",
395
+ "shows what the guide was distilled from. Freshness: run `sdlc_check.py stale`.",
396
+ "",
397
+ "| Guide | Status | When to consult | Source | Source version |",
398
+ "|---|---|---|---|---|"]
399
+ for rel, p, meta, _ in list_guides(root):
400
+ lines.append("| `{}` | {} | {} | {} | {} |".format(
401
+ p.name, meta.get("status", "-") or "-",
402
+ (meta.get("description", "") or "-").replace("|", "\\|"),
403
+ (meta.get("source", "") or "-").replace("|", "\\|"),
404
+ meta.get("source_version", "") or "-"))
405
+ return "\n".join(lines) + "\n"
406
+
407
+
358
408
  def cmd_index(root):
359
409
  if not require_ai_docs(root, "index"):
360
410
  return 1
@@ -369,6 +419,13 @@ def cmd_index(root):
369
419
  print(f"[ok] document manifest regenerated: {manifest}")
370
420
  else:
371
421
  print("[info] no canonical documents: INDEX.md not generated")
422
+ guides = list_guides(root)
423
+ gidx = root / "ai_docs" / "reference" / "INDEX.md"
424
+ if guides:
425
+ gidx.write_text(build_guide_index(root), encoding="utf-8")
426
+ print(f"[ok] guide router regenerated: {gidx}")
427
+ elif gidx.is_file():
428
+ print(f"[warn] {gidx} exists but no GUIDE_*.md found: stale router, remove or add guides")
372
429
  return 0
373
430
 
374
431
 
@@ -464,6 +521,38 @@ def cmd_validate(root, strict=False):
464
521
  or os.path.basename(other) == base) and ost == "CURRENT":
465
522
  warnings.append(f"{other}: still CURRENT but superseded by {rel} (set status: SUPERSEDED)")
466
523
 
524
+ # Guide checks (ai_docs/reference/GUIDE_*.md): structure only — freshness is stale's job
525
+ guides = list_guides(root)
526
+ for rel, p, meta, text in guides:
527
+ missing = [k for k in GUIDE_PROVENANCE_KEYS if not meta.get(k)]
528
+ if missing:
529
+ warnings.append(f"{rel}: guide missing provenance key(s): {', '.join(missing)}")
530
+ # (b) per-section fidelity markers: every '## ' section body must carry a marker
531
+ body = text.split("---", 2)[-1]
532
+ sections = re.split(r"^##\s+", body, flags=re.M)[1:]
533
+ unmarked = [s.splitlines()[0].strip() for s in sections if not GUIDE_MARKER_RE.search(s)]
534
+ if unmarked:
535
+ warnings.append(f"{rel}: section(s) without [source: ...] / [not covered by source] marker: "
536
+ + "; ".join(unmarked[:5]))
537
+ # (c) distilled_from confinement — fail closed (P-TM T6, distilled_from vector)
538
+ df = meta.get("distilled_from", "")
539
+ if df:
540
+ if Path(df).is_absolute() or ".." in Path(df).parts:
541
+ errors.append(f"{rel}: distilled_from '{df}' is absolute or escapes the project (..): rejected")
542
+ else:
543
+ target = (root / df).resolve()
544
+ try:
545
+ target.relative_to(root.resolve())
546
+ except ValueError:
547
+ errors.append(f"{rel}: distilled_from '{df}' resolves outside the project root: rejected")
548
+ # guide-router alignment (mirror of the root-manifest check)
549
+ gidx = root / "ai_docs" / "reference" / "INDEX.md"
550
+ if guides:
551
+ if not gidx.is_file():
552
+ errors.append("ai_docs/reference/INDEX.md missing: run 'sdlc_check.py index'")
553
+ elif norm_text(read_text(gidx)) != norm_text(build_guide_index(root)):
554
+ errors.append("ai_docs/reference/INDEX.md not aligned with the guides: run 'sdlc_check.py index'")
555
+
467
556
  # Handoff: header and freshness
468
557
  hand = ai / "audit" / "handoff.md"
469
558
  if hand.is_file():
@@ -515,14 +604,33 @@ def parse_audit_plan(root):
515
604
 
516
605
 
517
606
  def cmd_stale(root, hybrid=False):
607
+ rc = 0
608
+ # --- guide freshness (source_hash vs snapshot) — runs in EVERY mode
609
+ drifted = []
610
+ for rel, p, meta, _ in list_guides(root):
611
+ df, rec = meta.get("distilled_from", ""), meta.get("source_hash", "")
612
+ if not df or not rec:
613
+ continue # structure problems are validate's job
614
+ src = root / df
615
+ if not src.is_file():
616
+ print(f"[warn] {rel}: distilled_from '{df}' not found — snapshot missing")
617
+ rc = 1
618
+ continue
619
+ if sha256_file(src) != rec:
620
+ drifted.append((rel, df))
621
+ for rel, df in drifted:
622
+ print(f"[stale] {rel}: source snapshot '{df}' changed since distillation — regenerate the guide")
623
+ if drifted:
624
+ rc = 1
625
+ # --- audit-plan staleness — delegated to devPNT/KL in hybrid
518
626
  if hybrid:
519
627
  print("[info] hybrid mode: audit-plan staleness is delegated to devPNT/KL, skipping.")
520
- return 0
628
+ return rc # was: implicit skip-all; guide rc survives
521
629
  f, _, rows = parse_audit_plan(root)
522
630
  if not rows:
523
631
  print(f"[info] no rows in {f}: nothing to check "
524
632
  "(audit not initialized, or Hybrid mode where mapping is delegated to devPNT).")
525
- return 0
633
+ return rc # was: return 0 — MUST carry guide rc
526
634
  use_git = git_available(root)
527
635
  stale = []
528
636
  for row in rows:
@@ -554,7 +662,7 @@ def cmd_stale(root, hybrid=False):
554
662
 
555
663
  if not stale:
556
664
  print("[ok] no analyzed area was modified after its last recorded analysis.")
557
- return 0
665
+ return rc # was: return 0 — MUST carry guide rc
558
666
  print("Areas modified after the last recorded analysis:")
559
667
  for rel, changed in stale:
560
668
  print(f" {rel} ({len(changed)} files)")
@@ -563,7 +671,7 @@ def cmd_stale(root, hybrid=False):
563
671
  if len(changed) > 10:
564
672
  print(f" ... and {len(changed) - 10} more")
565
673
  print("\nAfter re-analyzing, record it with: sdlc_check.py mark <path>")
566
- return 1
674
+ return 1 # stale areas dominate: rc already implied
567
675
 
568
676
 
569
677
  def cmd_mark(root, paths):
@@ -20,6 +20,44 @@ supersedes: old_doc.md # only if it replaces another canonical doc
20
20
 
21
21
  When a doc replaces another: the new one declares `supersedes:`, the old one switches to `status: SUPERSEDED` (it stays as history, do not delete it). `sdlc_check.py validate` warns if `status` is missing or if a superseded doc is still `CURRENT`.
22
22
 
23
+ ## ai_docs/reference/GUIDE_[topic].md
24
+
25
+ Operative guide distilled from USER-PROVIDED indications (never from model knowledge).
26
+ The guide is a SYNTHESIS — the compact training a reader takes in whole before acting;
27
+ the verbatim snapshot in `ai_docs/reference/.sources/<slug>-<hash8>.md` is the book,
28
+ reached on demand. `source_hash` is the snapshot's SHA-256. Every `##` section carries
29
+ a fidelity marker: `[source: <snapshot-file>#<anchor-or-line>]` for covered content
30
+ (doubling as the detail-lookup pointer into the book), or a literal
31
+ `[not covered by source]` for gaps. Sections are CHOSEN from the repertoire below —
32
+ only those the source actually supports; never force empty ones. A guide approaching
33
+ the source's own length is a paraphrase, not a synthesis.
34
+
35
+ ```markdown
36
+ ---
37
+ description: One line, ≤160 chars — when to consult this guide.
38
+ status: CURRENT
39
+ source: Human-readable name of what the user provided.
40
+ source_version: v1.2 # optional — only when the origin is versioned
41
+ distilled_from: ai_docs/reference/.sources/topic-a1b2c3d4.md
42
+ source_hash: <sha256 of the snapshot file>
43
+ ---
44
+ # Guide: [Topic]
45
+
46
+ ## How to do [X]
47
+ [source: topic-a1b2c3d4.md#setup]
48
+ <!-- operative steps, imperative voice -->
49
+
50
+ ## How to verify it is done right
51
+ [source: topic-a1b2c3d4.md#checks]
52
+
53
+ ## What NOT to do
54
+ [not covered by source]
55
+ <!-- the user's material does not address this: do not invent. -->
56
+ ```
57
+
58
+ Section repertoire (pick what the source supports): How to do X / How to verify /
59
+ What NOT to do / What to watch out for / Core principles / When this applies.
60
+
23
61
  ## ai_docs/README.md
24
62
 
25
63
  Curated must-read index, by hand (it is NOT the generated manifest). Created at init, updated rarely, only for real must-reads.