@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 +21 -0
- package/README.md +82 -81
- package/gemini-extension.json +1 -1
- package/package.json +2 -1
- package/skills/agentic-sdlc-skill/SKILL.md +22 -3
- package/skills/agentic-sdlc-skill/guides.md +160 -0
- package/skills/agentic-sdlc-skill/scripts/sdlc_check.py +112 -4
- package/skills/agentic-sdlc-skill/templates.md +38 -0
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
|
-
├──
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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.
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antoneeo/agentic-sdlc-skill",
|
|
3
|
-
"version": "1.
|
|
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)
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
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.
|