@antoneeo/agentic-sdlc-skill 1.45.0 → 1.48.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 +41 -0
- package/README.md +3 -3
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/scripts/lib.js +109 -46
- package/scripts/postinstall.js +11 -1
- package/scripts/preuninstall.js +17 -1
- package/skills/agentic-sdlc-skill/ENFORCEMENT.md +59 -8
- package/skills/agentic-sdlc-skill/SKILL.md +8 -6
- package/skills/agentic-sdlc-skill/guides.md +7 -1
- package/skills/agentic-sdlc-skill/memory.md +1 -1
- package/skills/agentic-sdlc-skill/routing.md +4 -0
- package/skills/agentic-sdlc-skill/scripts/docs_site.py +72 -3
- package/skills/agentic-sdlc-skill/scripts/knowledge.py +519 -17
- package/skills/agentic-sdlc-skill/scripts/sdlc_core.py +569 -69
- package/skills/agentic-sdlc-skill/templates.md +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
Tutte le modifiche significative a questa skill saranno documentate in questo file.
|
|
4
4
|
|
|
5
|
+
## [1.48.0] - 2026-10-23
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- `guides.md`: a document snapshot is verbatim except for credentials and personal or confidential data the guide does not need, which become `[REDACTED: <kind>]` before the snapshot is written; the guide lists the kinds in `redacted_at_intake:`.
|
|
9
|
+
- The session-start orientation (`orient`) now fits under Claude Code's 10,000-character hook limit, even in a project with many documents. Above that limit Claude Code saves the output to a file and shows the agent only the first 2,000 characters, so the guide router never reached the agent. The output is now at most 9,500 characters, including the knowledge layer's lines, and in this order: the triage line, the guide router (one line per guide), the workstream registry (one line per workstream, then the project-wide notes), then the reading guide (README). The manifest (INDEX) and the full files are listed as pointers. A section that does not fit is cut at a line, with a pointer to its file.
|
|
10
|
+
- The per-turn `remind` line also tells the agent, above L1 and when sibling lenses are installed, to choose the lens with `routing.md` and declare it. The line's limit rises from 500 to 600 characters (`vision/rulings.md` r32).
|
|
11
|
+
- `docs_site.py` adds `.docs-site/` to the project's `.gitignore` on its first run in a git repository, so the site's copy of the documents is not committed. It used to print a note only. An entry already in the file is never changed; when git still does not ignore the folder, the note is printed as before.
|
|
12
|
+
- `SKILL.md` names the docs site in the words users use ("docs site", "minisito", "documentation site", "project site"), so a request for it runs `docs_site.py` instead of building a page by hand.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `check` refuses, in every lens, a line shaped like a credential (a password, token or key with a real value, a private-key header, a password in a URL, a bearer token) in a `corpus/given/` file or in the snapshot of a `source_kind: document` guide. The message names the file, the lines and the kinds, never the value. A human clears a false positive with `credential_scan: reviewed — <reason>` (in the sidecar, or in the guide). A project whose stored sources already hold such a line fails `check` until each is redacted or reviewed (`vision/rulings.md` r35a).
|
|
16
|
+
- `adopt`: a dry run listing the document guides that rest on `.sources/` snapshots and what moving each into `corpus/given/` involves. It writes nothing.
|
|
17
|
+
- `check` warns when the same text is stored both as a guide snapshot and as a corpus artifact.
|
|
18
|
+
- `check` also warns on a `corpus/given/` file with no sidecar, accepts a withdrawn artifact's tombstone sidecar (no bytes, no digest), and, when an `original_path:` is a URI, names `original_ref:` for non-local identifiers. A password inside a URL in that warning is masked.
|
|
19
|
+
- Shared claim checks (`check`): a claim's `about` cell may hold several relations separated by `; `, and one reserved predicate, `not-yet-honored-by -> ANALYSIS_<name>`, marks a specification claim the code does not yet honor. `check` refuses the marker when that analysis is missing from `solutions/` or cites neither the claim id nor the id of a row the claim succeeds. `export` drops the markers and says so in one line. Nothing lists or counts claims by the marker (`vision/rulings.md` r33). `memory.md`: a marked claim is cited as not yet true of the code, naming its analysis.
|
|
20
|
+
- Shared claim checks (`check`): the `not-yet-honored-by` marker now works in Hybrid projects (devPNT present). It may name `SHADOW_<doc_key>`; `check` reads the newest `solutions/SHADOW_<doc_key>_vX.Y.md` (versions compared number by number), which must exist and cite the claim id or a predecessor's. A missing shadow is an error that says to export it, and names a file of that key whose name lacks the `_vX.Y` suffix. `check` behaves the same with or without `--hybrid`; export drops both target forms (`vision/rulings.md` r36). New shared helper `sdlc_core.newest_shadow`. `memory.md`: a marked claim is cited naming its target. `SKILL.md` Write Triggers: the marker row no longer says "(Standalone)"; in Hybrid the claim id goes in the E-ISP/E-TDD and its exported shadow.
|
|
21
|
+
- Write Triggers: a Standalone L3 that defers fixing code which contradicts a `topics/` claim marks the claim and cites its id in its ANALYSIS. The L2/L3 change that makes the code honor the claim removes the marker; L1 is exempt.
|
|
22
|
+
- `verdict --hook`, wired by default on Claude Code: an Edit, Write, MultiEdit or NotebookEdit is refused, with one line saying how to unblock, when the session declared no guide-router verdict (or `Level: L1`), or never read a guide its verdict names. Silent in projects without guides, for subagents and whenever it cannot tell. Remove the hook entry to opt out (`ENFORCEMENT.md` §3, `vision/rulings.md` r34).
|
|
23
|
+
- `orient` adds a note when one client runs it from two settings entries, which means each session gets the orientation twice. Claude Code and Codex are counted separately, an identical command counts once, and an entry whose validator is gone is not counted.
|
|
24
|
+
- `orient` adds a line naming the sibling lenses installed beside the skill or in the user's Claude skills directory, and says to choose the lens with `routing.md`. The family list is read from `routing.md` §0, as the installer does.
|
|
25
|
+
- `routing.md` §2 gains worked verdicts for four mixed requests: an interface spec written for another team, the requirements another team must build, a guide whose source spec changed, and code that diverges from a supplied spec.
|
|
26
|
+
- The L3 ANALYSIS template gains a `## Guide check` section: two lines answering the Phase-3 question, whether a CURRENT guide covers what the change touches, and whether understanding it forced a model of a complex component that no guide covers. `validate` adds an advisory when an L3 analysis started on or after 2026-10-24 lacks the section or leaves it unfilled; older analyses are not checked.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- Uninstalling now also removes the per-turn `remind` hook entry. Left behind, it pointed at a deleted file and Python exited 2, which blocks the prompt.
|
|
30
|
+
|
|
31
|
+
## [1.47.0] - 2026-10-19
|
|
32
|
+
|
|
33
|
+
Includes everything listed under [1.46.0], which was tagged but never published to npm.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- `mark <paths> --note "<what was done>"` writes a dated entry `**YYYY-MM-DD — <what was done>.**` at the top of `audit/project_notes.md`. A release or work outside an open unit used to leave no dated event, because the date had to be copied there by hand. Without `--note`, and with no open unit, `mark` says so.
|
|
37
|
+
- `validate`: an advisory for a dated entry in `project_notes.md` whose bold does not pair; the chronology then showed the label running into the body.
|
|
38
|
+
- Docs site chronology: a Diary entry's label carries its unit id ("F-074 — closed"), so it reads on its own.
|
|
39
|
+
- `stale` no longer counts files inside the docs root (`ai_docs/`, `mkt_docs/`) as drift of any area, and `mark` refuses to record it: `validate` already checks the docs root, and every document edit used to leave `check` NOT CLEAN until a redundant re-mark.
|
|
40
|
+
|
|
41
|
+
## [1.46.0] - 2026-10-19
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- Docs site: `docs_site.py --zip <file>` also writes the site as one .zip to share; unzipped, `index.html` opens on any system. A zip made by hand with Windows PowerShell 5.1 `Compress-Archive` stores `data\core.js`, and on Linux or macOS the page then finds no data; `--zip` always writes `/` names. It refuses a target inside the docs root or the site folder. The `SKILL.md` line names it.
|
|
45
|
+
|
|
5
46
|
## [1.45.0] - 2026-10-19
|
|
6
47
|
|
|
7
48
|
Includes everything listed under [1.44.0], which was tagged but never published to npm.
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Vendoring requires all three files: the entry point, `sdlc_core.py`, `knowledge.
|
|
|
35
35
|
- **Independent review, twice**: the design is reviewed before it is implemented and the diff before the work is declared done — by somebody other than its author, who receives the whole review mandate in a fixed block copied verbatim, never a paraphrase of it. Three rungs of independence (fresh-context subagent > one-shot run > a declared self-pass, legal only when no higher rung is usable: absent, or permission-gated and declined — a gated rung is asked about, never silently skipped, and the log says which), capped at 3 rounds, one log line per review, and a PASS is invalid on "found nothing" — it must state where each constraint is satisfied.
|
|
36
36
|
- **Question discipline**: a real doubt is asked when it emerges, before its answer is written into any document — grouped, with the pros and cons of each option. A choice whose pros clearly win is taken without asking, and the weighing is written next to it; a fact is settled by evidence or asked, never weighed. A question is legal only when the agent searched first, names the search with its result, and names the decision it unblocks — never a stream of "shall I proceed?".
|
|
37
37
|
- **Installed support files**: Claude, Codex, Gemini, and Google Antigravity receive the full skill folder, including `templates.md`, `architect.md`, `guides.md`, `vision.md`, `tdd.md`, `debugging.md`, `elicitation.md`, `review.md`, `dispatch.md`, `routing.md`, `hybrid.md`, `ENFORCEMENT.md`, `memory.md`, and the validator's three files, `scripts/sdlc_check.py` + `scripts/sdlc_core.py` + `scripts/knowledge.py` — plus the docs site generator `scripts/docs_site.py` and its page `docs_site/`, and the `LICENSE` and `NOTICE` it ships under.
|
|
38
|
-
- **Docs site** (`scripts/docs_site.py --root <project> [--open]`): a static, offline, read-only website of what the agent reads in the project — reading guide, Vision, rules, documents, open work with each next step, analyses and, with a knowledge base, the topic map, each fact with its exact source passage, and the source documents. Written outside `ai_docs/`; nothing is ranked or recommended.
|
|
38
|
+
- **Docs site** (`scripts/docs_site.py --root <project> [--open] [--zip <file>]`): a static, offline, read-only website of what the agent reads in the project — reading guide, Vision, rules, documents, open work with each next step, analyses and, with a knowledge base, the topic map, each fact with its exact source passage, and the source documents. Written to `.docs-site/` (added to `.gitignore` on the first run), outside `ai_docs/`; nothing is ranked or recommended.
|
|
39
39
|
- **Mechanical checks**: optional validator for document structure, generated feature history, stale audit areas, and protected-path gates — `check`, `validate`, `index`, `stale`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`, `review-map` (code lens: classes the files of a change for the merge review brief). `benefit` reports what the review gates have already caught, beside what the doctrine costs to read — it always exits 0 and carries no verdict, because a measurement that can fail a build becomes a target.
|
|
40
40
|
|
|
41
41
|
## Installation
|
|
@@ -74,8 +74,8 @@ agentic-sdlc-init
|
|
|
74
74
|
Run it inside a project to create `ai_docs/`, Vision stubs awaiting your interview, strategic docs, audit plan, and agent protocol files (`AGENTS.md` — also the Antigravity CLI surface, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`).
|
|
75
75
|
|
|
76
76
|
It also **wires the SessionStart orientation hook** into `.claude/`, so an agent opening
|
|
77
|
-
the project is handed the
|
|
78
|
-
|
|
77
|
+
the project is handed the triage line, the guide router, the workstream registry and the
|
|
78
|
+
reading guide (with pointers to the manifest) before it does anything — instead of only when it remembers to look. That
|
|
79
79
|
wiring used to be a manual step documented in `ENFORCEMENT.md`; being manual, it was
|
|
80
80
|
skipped, and a project could run fully governed with an agent that never met the process.
|
|
81
81
|
|
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.48.0",
|
|
4
4
|
"description": "Documentation-First SDLC protocol for Claude Code, Gemini CLI, Google Antigravity and Codex with risk triage, Vision governance, installed support files and optional devPNT integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
package/scripts/lib.js
CHANGED
|
@@ -587,10 +587,23 @@ function wireGlobalOrientHook(options) {
|
|
|
587
587
|
// already removed the hook, the line stays and the opt-out survives uninstall,
|
|
588
588
|
// reinstall, and sibling-lens uninstalls.
|
|
589
589
|
function removeGlobalOrientHooks(removedRoots) {
|
|
590
|
+
return removeGlobalHookEntries(removedRoots, {
|
|
591
|
+
event: 'SessionStart',
|
|
592
|
+
re: / orient(\s|$)/,
|
|
593
|
+
markerTargets: orientMarkerTargets,
|
|
594
|
+
markerRemove: orientMarkerRemove,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// The pruning loop shared by every hook this installer wires (F-076): only the
|
|
599
|
+
// `spec.event` entries whose command matches `spec.re` AND whose validator
|
|
600
|
+
// token resolves INTO a removed root are ours; the marker line is cleared only
|
|
601
|
+
// when an entry was actually removed. Returns the targets touched.
|
|
602
|
+
function removeGlobalHookEntries(removedRoots, spec) {
|
|
590
603
|
const targets = [];
|
|
591
604
|
const claude = CLIENTS.find((c) => c.key === 'claude');
|
|
592
605
|
if (claude) targets.push(path.join(claude.home, 'settings.json'));
|
|
593
|
-
for (const t of
|
|
606
|
+
for (const t of spec.markerTargets()) {
|
|
594
607
|
if (!targets.some((x) => pathsEqual(x, t))) targets.push(t);
|
|
595
608
|
}
|
|
596
609
|
const removedFrom = [];
|
|
@@ -599,17 +612,17 @@ function removeGlobalOrientHooks(removedRoots) {
|
|
|
599
612
|
const state = orientSettingsState(target);
|
|
600
613
|
if (!state.ok || !fs.existsSync(target)) continue;
|
|
601
614
|
const hooks = state.settings.hooks;
|
|
602
|
-
if (!hooks || !Array.isArray(hooks.
|
|
615
|
+
if (!hooks || !Array.isArray(hooks[spec.event])) continue;
|
|
603
616
|
let removedHere = false;
|
|
604
617
|
const ours = (h) => {
|
|
605
618
|
const cmd = h && typeof h.command === 'string' ? h.command : '';
|
|
606
|
-
if (!(ORIENT_ENTRY_POINTS.some((n) => cmd.includes(n)) &&
|
|
619
|
+
if (!(ORIENT_ENTRY_POINTS.some((n) => cmd.includes(n)) && spec.re.test(cmd))) return false;
|
|
607
620
|
const tok = orientHookValidator(cmd);
|
|
608
621
|
if (tok === null || !path.isAbsolute(tok)) return false; // cannot attribute: keep
|
|
609
622
|
return removedRoots.some((root) => pathInside(tok, root));
|
|
610
623
|
};
|
|
611
624
|
const kept = [];
|
|
612
|
-
for (const group of hooks.
|
|
625
|
+
for (const group of hooks[spec.event]) {
|
|
613
626
|
if (!group || typeof group !== 'object') { kept.push(group); continue; }
|
|
614
627
|
if (Array.isArray(group.hooks)) {
|
|
615
628
|
const inner = group.hooks.filter((h) => {
|
|
@@ -626,13 +639,13 @@ function removeGlobalOrientHooks(removedRoots) {
|
|
|
626
639
|
}
|
|
627
640
|
}
|
|
628
641
|
if (!removedHere) continue;
|
|
629
|
-
state.settings.hooks.
|
|
630
|
-
if (!kept.length) delete state.settings.hooks.
|
|
642
|
+
state.settings.hooks[spec.event] = kept;
|
|
643
|
+
if (!kept.length) delete state.settings.hooks[spec.event];
|
|
631
644
|
if (state.settings.hooks && !Object.keys(state.settings.hooks).length) {
|
|
632
645
|
delete state.settings.hooks;
|
|
633
646
|
}
|
|
634
647
|
fs.writeFileSync(target, JSON.stringify(state.settings, null, 2) + String.fromCharCode(10), 'utf8');
|
|
635
|
-
|
|
648
|
+
spec.markerRemove(target);
|
|
636
649
|
removedFrom.push(target);
|
|
637
650
|
} catch (e) { /* fail-open per target: never abort an uninstall */ }
|
|
638
651
|
}
|
|
@@ -647,94 +660,142 @@ function removeGlobalOrientHooks(removedRoots) {
|
|
|
647
660
|
// already there -- which is the state of every existing installation.
|
|
648
661
|
// Owner-accepted per-turn cost: `ai_docs/vision/rulings.md` r19.
|
|
649
662
|
|
|
650
|
-
|
|
663
|
+
// F-076 generalized the pair below to every constant-command hook this
|
|
664
|
+
// installer wires: one spec per hook, the remind names kept as wrappers.
|
|
665
|
+
const REMIND_HOOK = {
|
|
666
|
+
event: 'UserPromptSubmit', sub: 'remind', re: / remind(\s|$)/,
|
|
667
|
+
marker: 'remind-hook-wired', timeout: 5,
|
|
668
|
+
};
|
|
669
|
+
// F-076: the guide-verdict check. `; exit 0` because a validator that cannot
|
|
670
|
+
// run `verdict` -- missing (npm 7+ may never run the uninstaller) or older than
|
|
671
|
+
// it (a downgrade) -- exits 2, and exit 2 on PreToolUse blocks every edit.
|
|
672
|
+
// Probed in Git Bash and PowerShell 5.1. Owner-accepted: rulings.md r34.
|
|
673
|
+
const VERDICT_HOOK = {
|
|
674
|
+
event: 'PreToolUse', sub: 'verdict --hook', suffix: '; exit 0', re: / verdict(\s|$)/,
|
|
675
|
+
marker: 'verdict-hook-wired', timeout: 10, matcher: 'Edit|Write|MultiEdit|NotebookEdit',
|
|
676
|
+
};
|
|
651
677
|
|
|
678
|
+
function commandHookCommand(spec, python, validator) {
|
|
679
|
+
return python + ' "' + validator + '" ' + spec.sub + (spec.suffix || '');
|
|
680
|
+
}
|
|
652
681
|
function remindHookCommand(python, validator) {
|
|
653
|
-
return python
|
|
682
|
+
return commandHookCommand(REMIND_HOOK, python, validator);
|
|
654
683
|
}
|
|
655
684
|
|
|
656
|
-
function
|
|
657
|
-
const groups = settings && settings.hooks && settings.hooks.
|
|
685
|
+
function findCommandHook(settings, spec) {
|
|
686
|
+
const groups = settings && settings.hooks && settings.hooks[spec.event];
|
|
658
687
|
if (!Array.isArray(groups)) return null;
|
|
659
688
|
for (const group of groups) {
|
|
660
689
|
if (!group || typeof group !== 'object') continue;
|
|
661
690
|
const inner = Array.isArray(group.hooks) ? group.hooks : [group];
|
|
662
691
|
for (const h of inner) {
|
|
663
692
|
const cmd = h && typeof h.command === 'string' ? h.command : '';
|
|
664
|
-
if (ORIENT_ENTRY_POINTS.some((n) => cmd.includes(n)) &&
|
|
693
|
+
if (ORIENT_ENTRY_POINTS.some((n) => cmd.includes(n)) && spec.re.test(cmd)) {
|
|
665
694
|
return cmd;
|
|
666
695
|
}
|
|
667
696
|
}
|
|
668
697
|
}
|
|
669
698
|
return null;
|
|
670
699
|
}
|
|
700
|
+
function findRemindHook(settings) {
|
|
701
|
+
return findCommandHook(settings, REMIND_HOOK);
|
|
702
|
+
}
|
|
671
703
|
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
// not die on someone's settings file.
|
|
675
|
-
function remindMarkerPath() {
|
|
704
|
+
// One marker file per hook: removal is a standing opt-out for THAT hook only.
|
|
705
|
+
function markerPathOf(spec) {
|
|
676
706
|
const root = process.env.AGENTIC_SDLC_KB_ROOT
|
|
677
707
|
|| path.join(os.homedir(), '.agentic-sdlc');
|
|
678
|
-
return path.join(root,
|
|
708
|
+
return path.join(root, spec.marker);
|
|
679
709
|
}
|
|
680
|
-
function
|
|
710
|
+
function markerTargetsOf(spec) {
|
|
681
711
|
try {
|
|
682
|
-
return fs.readFileSync(
|
|
712
|
+
return fs.readFileSync(markerPathOf(spec), 'utf8')
|
|
683
713
|
.split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
|
|
684
714
|
} catch (e) {
|
|
685
715
|
return [];
|
|
686
716
|
}
|
|
687
717
|
}
|
|
688
|
-
function
|
|
689
|
-
return
|
|
718
|
+
function markerHasOf(spec, target) {
|
|
719
|
+
return markerTargetsOf(spec).some((t) => pathsEqual(t, target));
|
|
720
|
+
}
|
|
721
|
+
function markerAddOf(spec, target) {
|
|
722
|
+
try {
|
|
723
|
+
if (markerHasOf(spec, target)) return;
|
|
724
|
+
fs.mkdirSync(path.dirname(markerPathOf(spec)), { recursive: true });
|
|
725
|
+
fs.appendFileSync(markerPathOf(spec), target + String.fromCharCode(10), 'utf8');
|
|
726
|
+
} catch (e) { /* fail-open */ }
|
|
690
727
|
}
|
|
691
|
-
function
|
|
728
|
+
function markerRemoveOf(spec, target) {
|
|
692
729
|
try {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
730
|
+
const rest = markerTargetsOf(spec).filter((t) => !pathsEqual(t, target));
|
|
731
|
+
if (rest.length) {
|
|
732
|
+
fs.writeFileSync(markerPathOf(spec), rest.join(String.fromCharCode(10)) + String.fromCharCode(10), 'utf8');
|
|
733
|
+
} else {
|
|
734
|
+
fs.rmSync(markerPathOf(spec), { force: true });
|
|
735
|
+
}
|
|
696
736
|
} catch (e) { /* fail-open */ }
|
|
697
737
|
}
|
|
738
|
+
function remindMarkerHas(target) { return markerHasOf(REMIND_HOOK, target); }
|
|
739
|
+
function remindMarkerAdd(target) { markerAddOf(REMIND_HOOK, target); }
|
|
698
740
|
|
|
699
|
-
|
|
741
|
+
// Writes the hook into `target` (an already-chosen settings file) unless it is
|
|
742
|
+
// there or the user opted out. Returns a code, never throws: an installer must
|
|
743
|
+
// not die on someone's settings file. Order matters: the hook being PRESENT is
|
|
744
|
+
// `already`, and only its ABSENCE plus our marker means the user removed it --
|
|
745
|
+
// a standing opt-out. Marker-first would refuse to re-check a hook someone
|
|
746
|
+
// still has.
|
|
747
|
+
function wireCommandHookInto(spec, target, python, validator) {
|
|
700
748
|
if (!python) return { code: 'no-python' };
|
|
701
749
|
if (!validator || ORIENT_UNSAFE_IN_PATH.test(validator)) return { code: 'unsafe-path', validator };
|
|
702
750
|
const state = orientSettingsState(target);
|
|
703
751
|
if (!state.ok) return { code: 'malformed', target, why: state.why };
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
// PRESENT is `already`, and only its ABSENCE plus our marker means the user
|
|
707
|
-
// removed it -- a standing opt-out. Marker-first would refuse to re-check a
|
|
708
|
-
// hook someone still has.
|
|
709
|
-
const present = findRemindHook(settings0);
|
|
752
|
+
const settings = state.settings;
|
|
753
|
+
const present = findCommandHook(settings, spec);
|
|
710
754
|
if (present) return { code: 'already', target, command: present };
|
|
711
|
-
if (
|
|
712
|
-
const
|
|
713
|
-
const existing = null;
|
|
714
|
-
if (existing) return { code: 'already', target, command: existing };
|
|
715
|
-
const command = remindHookCommand(python, validator);
|
|
755
|
+
if (markerHasOf(spec, target)) return { code: 'opted-out', target };
|
|
756
|
+
const command = commandHookCommand(spec, python, validator);
|
|
716
757
|
if (!settings.hooks) settings.hooks = {};
|
|
717
|
-
if (!Array.isArray(settings.hooks.
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
758
|
+
if (!Array.isArray(settings.hooks[spec.event])) settings.hooks[spec.event] = [];
|
|
759
|
+
const group = { hooks: [{ type: 'command', command, timeout: spec.timeout }] };
|
|
760
|
+
if (spec.matcher) group.matcher = spec.matcher;
|
|
761
|
+
settings.hooks[spec.event].push(group);
|
|
721
762
|
try {
|
|
722
763
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
723
764
|
fs.writeFileSync(target, JSON.stringify(settings, null, 2) + String.fromCharCode(10), 'utf8');
|
|
724
765
|
} catch (e) {
|
|
725
766
|
return { code: 'write-failed', target, command, error: e.message };
|
|
726
767
|
}
|
|
727
|
-
|
|
768
|
+
markerAddOf(spec, target);
|
|
728
769
|
return { code: 'wired', target, command };
|
|
729
770
|
}
|
|
771
|
+
function wireRemindHookInto(target, python, validator) {
|
|
772
|
+
return wireCommandHookInto(REMIND_HOOK, target, python, validator);
|
|
773
|
+
}
|
|
730
774
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
function wireGlobalRemindHook(options) {
|
|
775
|
+
// The machine-global twins of wireGlobalOrientHook, same target and validator.
|
|
776
|
+
function wireGlobalCommandHook(spec, options) {
|
|
734
777
|
const client = options.client;
|
|
735
778
|
const validator = path.join(skillTarget(client), 'scripts', ORIENT_ENTRY_POINT);
|
|
736
779
|
if (!fs.existsSync(validator)) return { code: 'no-validator', validator };
|
|
737
|
-
return
|
|
780
|
+
return wireCommandHookInto(spec, path.join(client.home, 'settings.json'), options.python, validator);
|
|
781
|
+
}
|
|
782
|
+
function wireGlobalRemindHook(options) { return wireGlobalCommandHook(REMIND_HOOK, options); }
|
|
783
|
+
function wireGlobalVerdictHook(options) { return wireGlobalCommandHook(VERDICT_HOOK, options); }
|
|
784
|
+
|
|
785
|
+
// Uninstall twin for the constant-command hooks: one {target, event} per
|
|
786
|
+
// removal, so the uninstaller can say what it removed.
|
|
787
|
+
function removeGlobalCommandHooks(removedRoots) {
|
|
788
|
+
const removed = [];
|
|
789
|
+
for (const spec of [REMIND_HOOK, VERDICT_HOOK]) {
|
|
790
|
+
const targets = removeGlobalHookEntries(removedRoots, {
|
|
791
|
+
event: spec.event,
|
|
792
|
+
re: spec.re,
|
|
793
|
+
markerTargets: () => markerTargetsOf(spec),
|
|
794
|
+
markerRemove: (target) => markerRemoveOf(spec, target),
|
|
795
|
+
});
|
|
796
|
+
for (const target of targets) removed.push({ target, event: spec.event });
|
|
797
|
+
}
|
|
798
|
+
return removed;
|
|
738
799
|
}
|
|
739
800
|
|
|
740
801
|
|
|
@@ -750,6 +811,8 @@ module.exports = {
|
|
|
750
811
|
wireOrientHook,
|
|
751
812
|
wireRemindHookInto,
|
|
752
813
|
wireGlobalRemindHook,
|
|
814
|
+
wireGlobalVerdictHook,
|
|
815
|
+
removeGlobalCommandHooks,
|
|
753
816
|
remindMarkerHas,
|
|
754
817
|
remindMarkerAdd,
|
|
755
818
|
remindHookCommand,
|
package/scripts/postinstall.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const {
|
|
5
5
|
SKILL_SOURCE, CLIENTS, clientDetected, skillTarget, copyRecursive,
|
|
6
|
-
wireGlobalOrientHook, wireGlobalRemindHook, detectPython,
|
|
6
|
+
wireGlobalOrientHook, wireGlobalRemindHook, wireGlobalVerdictHook, detectPython,
|
|
7
7
|
} = require('./lib');
|
|
8
8
|
|
|
9
9
|
function installSkill(client) {
|
|
@@ -51,6 +51,16 @@ for (const client of CLIENTS) {
|
|
|
51
51
|
console.log(` (${rr.target}). One constant line per prompt; remove that hook`);
|
|
52
52
|
console.log(' entry to opt out — it will not be re-added.');
|
|
53
53
|
}
|
|
54
|
+
// F-076: the guide-verdict check -- refuses an edit when the session
|
|
55
|
+
// declared no guide-router verdict, or never read a guide it names.
|
|
56
|
+
// Silent on projects without guides; owner-accepted per-edit cost:
|
|
57
|
+
// ai_docs/vision/rulings.md r34.
|
|
58
|
+
const rv = wireGlobalVerdictHook({ client, python: detectPython() });
|
|
59
|
+
if (rv.code === 'wired') {
|
|
60
|
+
console.log('🔗 Guide-verdict check wired for every project on this machine');
|
|
61
|
+
console.log(` (${rv.target}). Edits wait for the Rule-Zero router verdict in projects`);
|
|
62
|
+
console.log(' with guides; remove that hook entry to opt out — it will not be re-added.');
|
|
63
|
+
}
|
|
54
64
|
const r = wireGlobalOrientHook({ client, python: detectPython() });
|
|
55
65
|
if (r.code === 'wired') {
|
|
56
66
|
console.log('🔗 Session orientation wired for every project on this machine');
|
package/scripts/preuninstall.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const {
|
|
4
|
+
const {
|
|
5
|
+
CLIENTS, skillTarget, removeGlobalOrientHooks, removeGlobalCommandHooks,
|
|
6
|
+
} = require('./lib');
|
|
5
7
|
|
|
6
8
|
function removeSkill(client) {
|
|
7
9
|
const target = skillTarget(client);
|
|
@@ -27,4 +29,18 @@ try {
|
|
|
27
29
|
}
|
|
28
30
|
} catch (e) { /* never abort an uninstall for a convenience */ }
|
|
29
31
|
|
|
32
|
+
// F-076: the per-turn reminder and the guide-verdict check name the same skill
|
|
33
|
+
// dirs; left behind they exit 2 (Python cannot open the file), which blocks the
|
|
34
|
+
// prompt or the edit. The verdict entry is also wired `; exit 0`, because npm 7+
|
|
35
|
+
// may not run this script at all.
|
|
36
|
+
const COMMAND_HOOK_NAMES = {
|
|
37
|
+
UserPromptSubmit: 'per-turn reminder',
|
|
38
|
+
PreToolUse: 'guide-verdict check',
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
for (const { target, event } of removeGlobalCommandHooks(CLIENTS.map(skillTarget))) {
|
|
42
|
+
console.log(`🧹 Removed the ${COMMAND_HOOK_NAMES[event] || event} hook entry (${target}).`);
|
|
43
|
+
}
|
|
44
|
+
} catch (e) { /* never abort an uninstall for a convenience */ }
|
|
45
|
+
|
|
30
46
|
CLIENTS.forEach(removeSkill);
|
|
@@ -14,17 +14,17 @@ python "<skill_dir>/scripts/sdlc_check.py" check
|
|
|
14
14
|
|
|
15
15
|
## 2. Check in CI (recommended for teams)
|
|
16
16
|
|
|
17
|
-
Copy all three validator files into the repository: `scripts/sdlc_check.py`, `scripts/sdlc_core.py`, and `scripts/knowledge.py`, keeping them side by side. Then add to the pipeline:
|
|
17
|
+
Copy all three validator files into the repository: `scripts/sdlc_check.py`, `scripts/sdlc_core.py`, and `scripts/knowledge.py`, keeping them side by side. Then add to the pipeline:
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
python tools/sdlc_check.py validate --strict
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
The validator ships as **three files**: the domain entry point, `sdlc_core.py` and `knowledge.py`. Keep all three together. The core alone still runs but omits shared-memory/KB checks and domain-specific overlays; it is not an equivalent CI gate. `check` includes the knowledge surface when present.
|
|
23
|
+
The validator ships as **three files**: the domain entry point, `sdlc_core.py` and `knowledge.py`. Keep all three together. The core alone still runs but omits shared-memory/KB checks and domain-specific overlays; it is not an equivalent CI gate. `check` includes the knowledge surface when present.
|
|
24
24
|
|
|
25
25
|
Effect: an unregenerated index, invalid frontmatter, a missing security section or incoherent states **block the pipeline** instead of relying on the agent's memory. `--strict` also fails on warnings and on a missing `ai_docs/`, so a wrong working directory cannot produce a green pipeline. This works because documents travel in the same PR as the code (Phase 5 rule).
|
|
26
26
|
|
|
27
|
-
Note: the copy in the repo is the authoritative one for CI; update it when you update the skill — all three files, together.
|
|
27
|
+
Note: the copy in the repo is the authoritative one for CI; update it when you update the skill — all three files, together.
|
|
28
28
|
|
|
29
29
|
**Projects whose docs root is not `ai_docs/`.** Pass `--docs-dir <name>` (e.g. `--docs-dir mkt_docs`) on any subcommand, or set `AGENTIC_SDLC_DOCS_DIR`. Without either, the validator walks up from the working directory and takes the nearest root it recognizes. If it finds two side by side — the shape of a half-finished migration — it refuses and names both rather than validating half a project and printing a verdict. `ai_docs/` remains the default and the recommended root: the parameter exists so a legacy tree can be read and migrated, not so a second one can be kept.
|
|
30
30
|
|
|
@@ -59,9 +59,60 @@ Semantics: exit code 2 + message on stderr ⇒ the write is blocked and the mess
|
|
|
59
59
|
- The hook assumes the working directory is the project root (standard behavior of Claude Code hooks).
|
|
60
60
|
- **Hybrid/devPNT projects**: add `--hybrid` to the gate command. Governed designs live in the devPNT DB, so the gate also unlocks when an approved E-TDD shadow (`ai_docs/solutions/SHADOW_*tdd*.md`, exported before implementation — see `hybrid.md` §Shadow discipline) is present. Without the flag the gate would block legitimate governed work. The flag is deliberately explicit: never auto-detected.
|
|
61
61
|
|
|
62
|
+
### Verdict check: `verdict` (PreToolUse -- wired by default)
|
|
63
|
+
|
|
64
|
+
Rule Zero makes the agent declare the level with the guide-router verdict and read the
|
|
65
|
+
guide it names before acting. A field agent edited without doing either and said nothing
|
|
66
|
+
had stopped it (F-076). `sdlc_check.py verdict --hook` reads the session transcript
|
|
67
|
+
Claude Code hands every hook and refuses an Edit, Write, MultiEdit or NotebookEdit when:
|
|
68
|
+
|
|
69
|
+
- the session shows no line of the agent's own text with `router:` (the verdict) and no
|
|
70
|
+
`Level: L1` (or another lens's level 1), or
|
|
71
|
+
- a guide the verdict names, present in `reference/`, never appeared in the input of a
|
|
72
|
+
tool call other than an edit (Read, Bash `cat`, Grep, a subagent brief all count).
|
|
73
|
+
|
|
74
|
+
A verdict whose text after `router:` begins with `no match` or `absent` names no guide.
|
|
75
|
+
The refusal is one line, shown to the agent, saying how to unblock. It is silent — the
|
|
76
|
+
edit proceeds — in a project without guides, for a file outside the project, for a
|
|
77
|
+
subagent's edit (one the client marks with `agent_id`, or one whose transcript is the
|
|
78
|
+
subagent's own), and whenever it cannot tell (no or oversized transcript, unknown format,
|
|
79
|
+
ambiguous docs root, any error). It never approves an edit: compliance leaves the user's
|
|
80
|
+
normal permission flow in place.
|
|
81
|
+
|
|
82
|
+
Cost (owner-accepted, `ai_docs/vision/rulings.md` r34, the "no ceremony ratchet"
|
|
83
|
+
Non-Goal on r19's precedent): ~0.2–0.5 s per edit and zero tokens when compliant; one
|
|
84
|
+
refusal of at most 250 characters and one retry when not. L1 pays nothing beyond writing
|
|
85
|
+
`Level: L1`, which Rule Zero already asks for. Residuals: edits through a subagent and
|
|
86
|
+
writes made with Bash are not checked (a subagent's edit the client does not mark is judged
|
|
87
|
+
on the orchestrator's verdict); a verdict anywhere earlier in the session counts.
|
|
88
|
+
|
|
89
|
+
Installing the package wires it machine-wide on Claude Code; **removing that hook entry
|
|
90
|
+
is a standing opt-out.** The command ends in `; exit 0`: a validator that cannot run
|
|
91
|
+
`verdict` — gone after an uninstall, or older after a downgrade — would exit 2, and exit
|
|
92
|
+
2 on PreToolUse blocks every edit. To wire it by hand:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"hooks": {
|
|
97
|
+
"PreToolUse": [
|
|
98
|
+
{
|
|
99
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
|
|
100
|
+
"hooks": [
|
|
101
|
+
{
|
|
102
|
+
"type": "command",
|
|
103
|
+
"command": "python \"C:\\Users\\<user>\\.claude\\skills\\agentic-sdlc\\scripts\\sdlc_check.py\" verdict --hook; exit 0",
|
|
104
|
+
"timeout": 10
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
62
113
|
## 4. SessionStart hook (orientation, recommended default)
|
|
63
114
|
|
|
64
|
-
Emits the `ai_docs/` orientation
|
|
115
|
+
Emits the `ai_docs/` orientation to stdout at session start, in the order an arriving agent needs it: the Rule-Zero triage line, the lens choice when sibling lenses are installed, the guide router (`reference/INDEX.md`, one line per guide), the workstream registry (`audit/handoff.md`, one line per workstream, then the project-wide notes), the reading guide (`README.md`), then pointers to the manifest (`INDEX.md`) and to the full files — so the agent begins already oriented instead of reading them only if it remembers to. The whole output stays under 9,500 characters: Claude Code injects a hook's output up to 10,000 and, above that, only a 2,000-character preview, which is how the router once never reached a session (F-075). A section that does not fit is cut at a line with a pointer to its file, and a note says when one client runs the hook from two settings entries. It is **fail-open**: a missing or unreadable doc is skipped, and it always exits 0 — a broken or empty `ai_docs/` never blocks the session. It is **zero-execution** (it reads and prints, never runs anything).
|
|
65
116
|
|
|
66
117
|
**Wire it on every project that has `ai_docs/` and a Python interpreter.** It was opt-in until v1.16.0 and the field result was the defect this level exists to prevent: the guide router stayed unread unless the user asked for it by hand, so guides were written and never consulted. Prompt-level placement (Rule Zero declares the router verdict; Phase 1 reads the router) carries the process on its own — this hook is the backstop that survives long contexts, compaction and a session that never enters Phase 1 explicitly. Skip it only where Python is unavailable, and know what you are trading.
|
|
67
118
|
|
|
@@ -155,7 +206,7 @@ Gemini CLI — wire the same command into its startup-hook mechanism if present;
|
|
|
155
206
|
**Usage notes:**
|
|
156
207
|
- The hook assumes the working directory is the project root (standard Claude Code hook behavior); it also accepts `--root <path>`.
|
|
157
208
|
- **Hybrid/devPNT projects**: add `--hybrid` — the hook then appends a one-line pointer to run `devpnt_mcp_get_bootstrap` for the Master Plan / Knowledge Layer and does not replicate them; the filesystem orientation (router + handoff + README) still emits.
|
|
158
|
-
- Like the CI gate (§2), if you copied the validator into the repo, the hook references that copy — keep all three files current when you update the skill.
|
|
209
|
+
- Like the CI gate (§2), if you copied the validator into the repo, the hook references that copy — keep all three files current when you update the skill.
|
|
159
210
|
|
|
160
211
|
### Per-turn reminder: `remind` (UserPromptSubmit -- wired by default)
|
|
161
212
|
|
|
@@ -166,13 +217,13 @@ a release ran against a `GUIDE_release.md` the session never consulted, because
|
|
|
166
217
|
ONE constant line at every prompt, and its first duty is that one: decide whether the
|
|
167
218
|
skill governs the work in front of you and, if it does, load it once.
|
|
168
219
|
|
|
169
|
-
Cost:
|
|
220
|
+
Cost: 566 characters, ~95 tokens per prompt, paid on every turn including
|
|
170
221
|
trivial ones. That is admissible only because the owner accepted it explicitly
|
|
171
222
|
(`ai_docs/vision/rulings.md` r19, the "no ceremony ratchet" Non-Goal's second door) --
|
|
172
223
|
and the acceptance was for a FLAT cost. Hence the contract, guarded by
|
|
173
224
|
`scripts/test_remind.py`: the line is **constant and reads nothing** (so nothing repo-
|
|
174
225
|
or session-controlled rides into the agent's context through it, and the cost cannot
|
|
175
|
-
grow with the session), it is **one line under
|
|
226
|
+
grow with the session), it is **one line under 600 characters** (r32 raised r19's 500 to carry the lens choice), and it **can never
|
|
176
227
|
break a prompt** (argv ignored whole, always exit 0). A trivial turn pays a yes/no
|
|
177
228
|
judgement, never a load.
|
|
178
229
|
|
|
@@ -215,7 +266,7 @@ It aggregates the test files (`test_plan.py` + `test_session_start.py` + `test_s
|
|
|
215
266
|
|
|
216
267
|
**Optional CI** (same shape as §2, not mandatory): add a `run:` step invoking the `unittest discover` command above.
|
|
217
268
|
|
|
218
|
-
**T10 note:** if you copied the validator (all three files) and the `test_*.py` battery into the repo for CI, that copy is authoritative — keep it current when you update the skill.
|
|
269
|
+
**T10 note:** if you copied the validator (all three files) and the `test_*.py` battery into the repo for CI, that copy is authoritative — keep it current when you update the skill.
|
|
219
270
|
|
|
220
271
|
## `benefit` is a report, never a CI gate
|
|
221
272
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentic-sdlc
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.48.0
|
|
4
4
|
description: Documentation-First SDLC protocol with risk-proportional triage, Vision as a guide, a complete Standalone mode and optional symbiosis with devPNT. Use for features, significant bugs, refactors, audits and documented maintenance.
|
|
5
5
|
author: Antonio Pinto (https://github.com/Antoneeo)
|
|
6
6
|
copyright: (c) 2026 Antonio Pinto
|
|
@@ -21,7 +21,8 @@ Support files in the skill directory:
|
|
|
21
21
|
- `hybrid.md`: the devPNT seam — the authoritative hierarchy, the ownership matrix (who owns what when both authorities are live), the triage equivalence, the feature-state mapping and the shadow discipline. Read it when the `devpnt_*` tools point at this project; a Standalone session never needs it.
|
|
22
22
|
- `routing.md`: which lens owns this unit of work. Read ONLY when a sibling lens skill is installed alongside this one; a single-lens install never reads it.
|
|
23
23
|
- `scripts/sdlc_check.py` + `scripts/sdlc_core.py`: the mechanical validator for the docs root (`check`, `validate`, `index`, `stale`, `mark`, `benefit`, `gate`, `plan`, `orient`, `migrate`). Keep the entry point, `scripts/sdlc_core.py` and `scripts/knowledge.py` together; the last supplies common memory and KB integrity.
|
|
24
|
-
- `scripts/docs_site.py --root <project> --open
|
|
24
|
+
- `scripts/docs_site.py --root <project> --open` (`--zip <file>` to share): opens in the user's browser a read-only site of what the agent reads; use it to show the project state.
|
|
25
|
+
It is what users mean by "docs site", "minisito", "documentation site" or "project site": run it, never build a page by hand.
|
|
25
26
|
- `ENFORCEMENT.md`: optional setup for CI and hooks.
|
|
26
27
|
|
|
27
28
|
Read these files only when needed. `SKILL.md` is the operating contract; the support files are progressive resources.
|
|
@@ -50,7 +51,7 @@ The validator now ships as three files: entry point, `sdlc_core.py`, `knowledge.
|
|
|
50
51
|
|
|
51
52
|
Always classify the request before choosing the process. Declare the chosen level to the user when you start operational work.
|
|
52
53
|
|
|
53
|
-
**Declare the level WITH the router verdict** (one line, for L2, L3 and Spike — L1 declares the level alone): the result of the guide-router lookup described under `## Operative Guides`, i.e. `Level: L2 · router: no match` or `Level: L3 · router: GUIDE_release.md → read`. The lookup is the consult trigger; making its result a declared output is what keeps it from being skipped — a level declared without a verdict makes "did not look" indistinguishable from "looked, nothing matched". Name the guide you matched, or `no match`; name a second one only when it covers a genuinely distinct concern (typically one operative guide plus the comprehension map of the component you are touching). The verdict is never a listing of the catalogue, and never an excuse to read every guide.
|
|
54
|
+
**Declare the level WITH the router verdict** (one line, for L2, L3 and Spike — L1 declares the level alone): the result of the guide-router lookup described under `## Operative Guides`, i.e. `Level: L2 · router: no match` or `Level: L3 · router: GUIDE_release.md → read`. The lookup is the consult trigger; making its result a declared output is what keeps it from being skipped — a level declared without a verdict makes "did not look" indistinguishable from "looked, nothing matched". Name the guide you matched, or `no match`; name a second one only when it covers a genuinely distinct concern (typically one operative guide plus the comprehension map of the component you are touching). The verdict is never a listing of the catalogue, and never an excuse to read every guide. On Claude Code with the package's hooks wired, an edit in a project with guides is refused until the session shows a verdict (or `Level: L1`) and has read every guide it names (`ENFORCEMENT.md` §3); other clients keep it a written duty.
|
|
54
55
|
|
|
55
56
|
| Level | Criteria | Required process |
|
|
56
57
|
|---|---|---|
|
|
@@ -98,6 +99,7 @@ Triage decides IF documentation is due; this table decides WHICH document each e
|
|
|
98
99
|
| `reference/GUIDE_[topic].md` (`source_kind: document`) | Origin+purpose test (`guides.md`), or a proactive proposal the user accepted. Propose, never a silent write, never from model knowledge. | 4 / 5 |
|
|
99
100
|
| `reference/GUIDE_[topic].md` (`source_kind: code`) | Recognized high-complexity component/feature/layer with no CURRENT guide — including one that breaks repeatedly across sessions → **duty to write autonomously** (no proposal; additive, code-anchored, reversible). Fidelity floor: every claim traces to a code excerpt. Signals + guard-rails: `guides.md` §1. Write it as soon as you recognize the signal; the Phase-5 Comprehension checkpoint is the backstop that asks the question, never the only moment it may fire. | 4 / 5 |
|
|
100
101
|
| `audit/reviews/REVIEW_LOG.md` | Every completed review — when and what to write is `review.md` §When a review is due; schema and column meanings are `templates.md`. | 3 / 5 |
|
|
102
|
+
| `topics/` claim marked `not-yet-honored-by -> ANALYSIS_<name>` (Hybrid: `SHADOW_<doc_key>`) | An L3 defers fixing code that contradicts a claim in `topics/`: mark the claim and cite its id in the ANALYSIS, in Hybrid in the E-ISP/E-TDD and its exported shadow (`check` verifies both). The L2/L3 change that makes the code honor it removes the marker in the same commit; L1 is exempt. Nothing lists marked claims. | 3 / 5 |
|
|
101
103
|
| ADR — `architecture/` (Standalone) or devPNT DB (Hybrid) | An architectural decision was taken (new pattern, layer or contract change, structural dependency): record it at closure, before DONE. No decision, no ADR. | 5 |
|
|
102
104
|
| `strategic/architecture.md`, `strategic/existing_features.md` | Bootstrap; update at closure when the stack or the feature catalog actually changed. | 1 / 5 |
|
|
103
105
|
| `strategic/architecture.md` — `## Component Map` | A component was BORN, its contract changed, **or the pass DISCOVERED an existing one while searching an unmapped area** — the same closure adds or corrects its row (`architect.md`). Keyed on the component, not on the stack: a new component is not a stack change, and a discovered one is how an area gets marked ANALYZED while the map stays silent about what lives there — after which the next feature may lawfully rule it MISSING and build it twice. | 5 |
|
|
@@ -141,7 +143,7 @@ devPNT is not there, work Standalone and nothing is lost.
|
|
|
141
143
|
|
|
142
144
|
- Read `ai_docs/audit/handoff.md` if it exists — the **workstream registry**: one row per open workstream, so you see at a glance what is in PROGRESS, on which branch, since when, before touching anything. It is generated from the `HANDOFF_*.md` files, so it is read here and never edited here. If a row's Date/Branch are inconsistent with reality, treat that row as history. When resuming a specific workstream, read its `audit/HANDOFF_[feature].md` (its row plus the resume logistics) AND its ANALYSIS Diary (the durable narrative) — the registry row points at both. A hand-written or pre-1.17 narrative handoff still works and is converted when you next write it, not now (Write Triggers).
|
|
143
145
|
- Read `ai_docs/README.md` (curated must-reads), `ai_docs/INDEX.md` (generated manifest of all canonical docs) and `ai_docs/reference/INDEX.md` (the guide router) to know what exists before exploring the code. The router is a mandatory read, not an optional one: it is the only orientation step that tells you a guide already governs the work you are about to do. On a project with no guides yet it exists as an empty stub (`sdlc_check.py index` writes it precisely so the mandatory read has something to read) — the honest verdict there is `router: no match`, and if the file is genuinely absent, say `router: absent (no router file)` and regenerate it rather than inventing a match. `solutions/` and `audit/` are not indexed per file: search them with glob/grep.
|
|
144
|
-
- Recommended default: a SessionStart hook (`ENFORCEMENT.md` §4) emits this orientation automatically at session start (
|
|
146
|
+
- Recommended default: a SessionStart hook (`ENFORCEMENT.md` §4) emits this orientation automatically at session start (triage line, lens choice when sibling lenses are installed, guide router, workstream registry, README, then pointers to INDEX and the full files — under the client's hook cap), so the router reaches the context even in a session that never opens Phase 1 explicitly. Wire it wherever Python is available; when it is not wired, do these reads manually as above — the process never depends on it, and it fails open (a missing/empty `ai_docs/` never blocks the session).
|
|
145
147
|
- If `ai_docs/` is missing or incomplete, create the structure and the **bootstrap set** by analyzing the project in batches: `README.md`, the three `vision/` docs as stubs awaiting the owner interview (`Status: DRAFT`; the agent never reconstructs a Vision — Write Triggers), `strategic/architecture.md`, `strategic/existing_features.md` and — Standalone — `audit/audit_plan.md`; then regenerate `INDEX.md`. Nothing else is mandatory at bootstrap (per-document triggers: Write Triggers).
|
|
146
148
|
- **Arriving in a project that was never curated** (the usual case — you arrive with a task, not with a bootstrap): write `audit/audit_plan.md` FIRST. It is the scope ledger the rest is built on — one row per area, all PENDING, `SKIPPED` for what genuinely does not merit reading (vendored, generated). Then the other bootstrap documents describe what you have actually analyzed, and the `## Component Map` in `strategic/architecture.md` starts at whatever the first task made you understand. **No full-codebase sweep is required before the first feature**: the map grows feature by feature, each one marking the areas it covered (`sdlc_check.py mark`). What is NOT deferred is comprehension of what the change touches or depends on — that is understood now, at full standard, mapped or not. The licence is about writing the inventory, never about designing on a guess (`architect.md` §2: unmapped is *unread*, not *empty*, and can never ground a MISSING verdict).
|
|
147
149
|
- In Standalone use `ai_docs/audit/audit_plan.md` for mapping and state.
|
|
@@ -178,7 +180,7 @@ For any L3, run the spec elicitation round in `elicitation.md` BEFORE drafting t
|
|
|
178
180
|
|
|
179
181
|
**Architect before you list files.** Once the spec is known and BEFORE drafting the Impact, run the architect pass in `architect.md`: state what the feature requires the system to be able to DO (capabilities — verbs over domain nouns, no files), rule each one against the platform — **EXISTS** (name the component and where it lives) / **INADEQUATE** (name the gap) / **MISSING** (say what you searched) — and design what is missing as a component with **its own contract, in its own vocabulary**, of which this feature is one consumer. The output goes where that mode keeps designs — Standalone: the ANALYSIS `## Capability Ledger`; Hybrid: the `E-ISP`, above its Impacted Components map (`architect.md`; never a second copy in `ai_docs/`) — and it feeds the Impact. Note the coverage asymmetry: the validator backstop reads Standalone ANALYSIS files only, so in Hybrid the sole check that the pass ran is `review.md`'s ledger clause — which is why that clause fires on a MISSING ledger and not only on the rows of one that is present. Why it is its own step: a feature is not a unit of construction, and an agent that skips it builds the missing capability inside the feature's code path, where no component owns it and the next feature rebuilds it differently. The pass is a question, not a form — when every capability plainly exists, one line answers it.
|
|
180
182
|
|
|
181
|
-
**Would a guide be worth writing here?** Ask it while you analyze — this is when you are paying to build the model, and the answer decides whether the next session inherits it or re-derives it. Two questions, one line each in the ANALYSIS: does a CURRENT guide already cover what I am about to touch, and did understanding this force a model of a high-complexity component that none covers (then writing it is a duty, `guides.md` §1)?
|
|
183
|
+
**Would a guide be worth writing here?** Ask it while you analyze — this is when you are paying to build the model, and the answer decides whether the next session inherits it or re-derives it. Two questions, one line each in the ANALYSIS `## Guide check` (`validate` notes an L3 that leaves it unanswered): does a CURRENT guide already cover what I am about to touch, and did understanding this force a model of a high-complexity component that none covers (then writing it is a duty, `guides.md` §1)?
|
|
182
184
|
|
|
183
185
|
**Blast-radius enumeration is an authoring duty, not a review finding.** Before writing the Impact (the list of what changes), for every symbol whose signature you change, thread a new field through, or that has more than one caller: mechanically enumerate EVERY consumer with the best symbol-graph tool your toolchain offers — an LSP/IDE call hierarchy or a find-usages / call-graph capability — with `grep` only as a last-resort fallback, and list the full set in the Impact. Anchor to symbol identity, not line numbers (they rot). This is deterministic and cheap: doing it up-front collapses the review into one pass, instead of the reviewer returning "you missed a consumer" one round at a time. Leaving completeness to the closure review is the myopia failure this whole workflow exists to prevent.
|
|
184
186
|
|
|
@@ -189,7 +191,7 @@ For any L3, run the spec elicitation round in `elicitation.md` BEFORE drafting t
|
|
|
189
191
|
Standalone L3:
|
|
190
192
|
- Before creating a new `ANALYSIS_[feature].md`, search `ai_docs/solutions/` with glob/grep for an existing analysis on the same topic: if there is one, update it instead of duplicating it.
|
|
191
193
|
- Create or update `ai_docs/solutions/ANALYSIS_[feature].md`.
|
|
192
|
-
- Minimum sections: Objective, Feature Vision (or Vision Alignment), Use Cases / User Needs, Functional Spec (conditional — behavior change, the paragraph above), Interface Contract (conditional — the paragraph above), Capability Ledger, Impact, Security and Threat Model, Action Plan, Test Strategy, Diary/Current State. (`review.md` makes an uncovered use-case a finding, so an ANALYSIS without that section fails its own closure review.)
|
|
194
|
+
- Minimum sections: Objective, Feature Vision (or Vision Alignment), Use Cases / User Needs, Functional Spec (conditional — behavior change, the paragraph above), Interface Contract (conditional — the paragraph above), Capability Ledger, Guide check, Impact, Security and Threat Model, Action Plan, Test Strategy, Diary/Current State. (`review.md` makes an uncovered use-case a finding, so an ANALYSIS without that section fails its own closure review.)
|
|
193
195
|
- Build the Impact/solution **on** the Vision, the use-cases/user-needs and the Security & Threat Model — read and trace to them first, and state the trace (which actor / use-case / threat / benefit each part serves) so the closure review (`review.md`) can verify conformance. Do not draft the Impact in isolation.
|
|
194
196
|
- For a feature known to span multiple milestones, also create `ai_docs/vision/features/VISION_[feature].md`; the retroactive case (extract it when the SECOND `ANALYSIS_*` on a theme appears) is in Write Triggers.
|
|
195
197
|
|
|
@@ -201,7 +201,13 @@ the component from partial understanding.
|
|
|
201
201
|
rewrites snapshot bytes at all (keeps snapshots byte-verbatim; the
|
|
202
202
|
normalized hash stays stable either way).
|
|
203
203
|
- The snapshot is verbatim: no paraphrasing, no reformatting beyond what is
|
|
204
|
-
needed to save it as markdown
|
|
204
|
+
needed to save it as markdown — with one exception, made before the file is
|
|
205
|
+
written and hashed: credentials, and personal or confidential third-party data
|
|
206
|
+
the guide does not need, become `[REDACTED: <kind>]`, and the guide's
|
|
207
|
+
frontmatter lists the kinds in `redacted_at_intake:`. A snapshot is committed
|
|
208
|
+
and hash-pinned, and `check` refuses a credential-shaped line in a document
|
|
209
|
+
guide's snapshot (a false positive is declared in the guide as
|
|
210
|
+
`credential_scan: reviewed — <reason>`).
|
|
205
211
|
- **For `source_kind: code`** the snapshot is the verbatim CODE EXCERPTS the guide
|
|
206
212
|
explains — the specific functions / classes / regions of the real files, each
|
|
207
213
|
labelled with its `path:symbol` (or `path:startLine-endLine`) — assembled into
|