@antoneeo/kb-agentic-skill 1.31.0 → 1.33.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 +63 -0
- package/README.md +58 -17
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/scripts/init.js +6 -6
- package/skills/kb-agentic-skill/ENFORCEMENT.md +3 -3
- package/skills/kb-agentic-skill/SKILL.md +18 -4
- package/skills/kb-agentic-skill/distillation.md +15 -5
- package/skills/kb-agentic-skill/docs_site/app.js +7 -1
- package/skills/kb-agentic-skill/docs_site/strings.js +9 -0
- package/skills/kb-agentic-skill/docs_site/style.css +2 -0
- package/skills/kb-agentic-skill/guides.md +8 -0
- package/skills/kb-agentic-skill/portability.md +52 -16
- package/skills/kb-agentic-skill/reconciliation.md +29 -8
- package/skills/kb-agentic-skill/review.md +37 -1
- package/skills/kb-agentic-skill/scripts/docs_site.py +17 -3
- package/skills/kb-agentic-skill/scripts/knowledge.py +925 -33
- package/skills/kb-agentic-skill/scripts/sdlc_core.py +609 -29
- package/skills/kb-agentic-skill/templates.md +49 -4
- package/skills/kb-agentic-skill/vision.md +308 -308
|
@@ -39,7 +39,8 @@ sdlc_check.py check # always, right after
|
|
|
39
39
|
and, if a topic was skipped, *"a topic with the same name already exists; I have not
|
|
40
40
|
merged them, because they may not be the same subject. Do you want me to look?"*
|
|
41
41
|
4. If an imported ruling arrived: *"one decision came from the other project. I have kept
|
|
42
|
-
it, marked as theirs, and it cannot settle anything here until you confirm it.
|
|
42
|
+
it, marked as theirs, and it cannot settle anything here until you confirm it. The
|
|
43
|
+
question it had closed is open again here, with their decision beside the sources."*
|
|
43
44
|
|
|
44
45
|
Do not explain the closure, the id hashing or the provenance model unless asked. The
|
|
45
46
|
user asked to move their knowledge, not to learn how it is stored.
|
|
@@ -74,6 +75,11 @@ You choose topics; the export decides what must travel with them.
|
|
|
74
75
|
When a partner row lives in an unselected topic, that topic is **added and reported** —
|
|
75
76
|
never dropped, never silently.
|
|
76
77
|
- **Not the `not-yet-honored-by` markers.** They describe this project's code, so the export drops them and says so, without a number.
|
|
78
|
+
- **Not the declarations.** A `DECLARED` row is this project's commitment to its own
|
|
79
|
+
recipient; carried elsewhere it would read as the importer's. The export leaves the
|
|
80
|
+
rows and the artifacts only they cite behind and says so in one line, without a
|
|
81
|
+
number. Nothing else is lost: no other row may point at a declaration, and the export
|
|
82
|
+
refuses one that does (run `check`).
|
|
77
83
|
|
|
78
84
|
If a conflict partner resolves to no row at all, the export **refuses**: exporting a
|
|
79
85
|
broken set is worse than exporting nothing.
|
|
@@ -83,7 +89,8 @@ tombstone sidecar stays home.
|
|
|
83
89
|
|
|
84
90
|
## 3. Import is additive, and decides nothing
|
|
85
91
|
|
|
86
|
-
`import` writes files. It does not place concepts, merge bodies, or settle disagreements
|
|
92
|
+
`import` writes files. It does not place concepts, merge bodies, or settle disagreements —
|
|
93
|
+
and it refuses to let the bundle settle them either: a ruling arrives foreign (§4).
|
|
87
94
|
|
|
88
95
|
- **Never overwrites a topic.** A slug that already exists is reported and skipped: two
|
|
89
96
|
projects using the word `pricing` may mean two different things, and that judgement is
|
|
@@ -96,7 +103,12 @@ tombstone sidecar stays home.
|
|
|
96
103
|
It refuses on: a missing or unmarked `MANIFEST.md`; a path that escapes the docs root; an
|
|
97
104
|
artifact whose name matches an existing one **with different bytes** (content-addressed
|
|
98
105
|
names must mean equal content — a mismatch means one of the two is lying about its
|
|
99
|
-
origin); an incomplete conflict set
|
|
106
|
+
origin); an incomplete conflict set — a `CONTESTED` or `SUPERSEDED` pointer that would
|
|
107
|
+
resolve to no row, or to a row of this project that does not name it back (an import
|
|
108
|
+
never edits an existing topic); a row settled by a ruling the import does not write, or
|
|
109
|
+
by a row this project holds as `IMPORTED`; a ruling it cannot carry foreign (§4); a
|
|
110
|
+
`DECLARED` row (an export never ships one, so the bundle was edited by hand). Each
|
|
111
|
+
refusal names the row and writes nothing.
|
|
100
112
|
|
|
101
113
|
After importing, run `check`. The import is deliberately not a validator.
|
|
102
114
|
|
|
@@ -108,19 +120,43 @@ another owner's decision, and importing it unchanged would make that decision bi
|
|
|
108
120
|
here without anyone here granting it — the machine deciding, which reconciliation
|
|
109
121
|
refuses everywhere else.
|
|
110
122
|
|
|
111
|
-
So
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
So `import` makes every `RULING` row of a topic it writes arrive as **`prov: IMPORTED`**
|
|
124
|
+
(owner rulings 2026-08-03 and 2026-10-23):
|
|
125
|
+
|
|
126
|
+
- **its note arrives as a renamed copy**, `corpus/notes/imported_<source project>_<name>`,
|
|
127
|
+
whose frontmatter adds `imported_from: <source project> — <original path>` and reads
|
|
128
|
+
`origin: imported`. Its text and original `basis:` travel **verbatim** — the knowledge
|
|
129
|
+
is not lost, and pretending it is a `DERIVED` synthesis would make the row lie about
|
|
130
|
+
where it came from. The note is never edited under its own name: a line inserted into
|
|
131
|
+
it would move the line every locator cites. The row cites the copy at the same text
|
|
132
|
+
(its locator and id move with it), and a bundle note `derived_from:` the ruling names
|
|
133
|
+
the copy. The original name is never written, so a row copied by hand out of a skipped
|
|
134
|
+
topic cannot cite a foreign note as local;
|
|
135
|
+
- **the question it had closed reopens.** The rows it superseded and the ruling itself
|
|
136
|
+
arrive as one `CONTESTED` set; a ruling already contested with a newer source keeps
|
|
137
|
+
that partner, one that superseded nothing arrives `OK`;
|
|
138
|
+
- the validator refuses an `IMPORTED` row whose note lacks `imported_from:` (unnamed,
|
|
139
|
+
the class says nothing), a `SUPERSEDED <id>` pointing at an `IMPORTED` row, and a
|
|
140
|
+
`RULING` row on a note that names a foreign origin.
|
|
141
|
+
|
|
142
|
+
**Ratification** is the ordinary ruling of `reconciliation.md`, and it is you deciding,
|
|
143
|
+
which is the only thing that ever settles anything here: write your own note with your
|
|
144
|
+
own `basis:`, add a `RULING` row on it, and mark every row `CONTESTED` with the imported
|
|
145
|
+
one — following the sets transitively — `SUPERSEDED <your id>`, the `IMPORTED` row
|
|
146
|
+
included. Relabelling the `IMPORTED` row `RULING` is refused: another project's note
|
|
147
|
+
cannot carry your decision.
|
|
148
|
+
|
|
149
|
+
Any bundle note that declares a `basis:` lands this way, renamed, even when its ruling row
|
|
150
|
+
does not travel with it — otherwise it would land first under its own name and a later
|
|
151
|
+
import of its ruling would find it there. A ruling `import` cannot carry this way is
|
|
152
|
+
refused, never copied: one not resting on a note cited by line, on a note with no
|
|
153
|
+
frontmatter, or on a note that already names a foreign origin (the source relabelled an
|
|
154
|
+
imported row); a row superseded both by a ruling and by a row that is not one; a ruling
|
|
155
|
+
another row superseded while rows still depend on it. A tree imported before this
|
|
156
|
+
conversion existed (≤ kb 1.31.0) holds the foreign ruling as `RULING` on its original
|
|
157
|
+
note: delete the imported topic and its files and import again, or convert it by hand as
|
|
158
|
+
above. The origin is named after the exporting folder (`source_project`), so two clones
|
|
159
|
+
of one project in differently named folders name it differently.
|
|
124
160
|
|
|
125
161
|
## 5. Triage
|
|
126
162
|
|
|
@@ -24,6 +24,15 @@ Subject-sameness is semantic judgement; no query performs it. Classify:
|
|
|
24
24
|
| **coexistence** | contradictory only if scopes overlapped — and by the half-open rule they do not | both rows stay `OK`. "12k until March" and "15k from March" are two truths, not a conflict |
|
|
25
25
|
| **conflict** | contradictory, scopes overlap | **every** row in the set becomes `CONTESTED` listing all counterparts. Nothing is picked |
|
|
26
26
|
|
|
27
|
+
**A declaration is not a source.** A `DECLARED` row (`distillation.md` §1: what this
|
|
28
|
+
project sent) never joins the five outcomes with a fact: it never corroborates a row,
|
|
29
|
+
never refines or supersedes one, never contests one. Against the ledger it is either
|
|
30
|
+
consistent (`OK`) or it **diverges**: its assertion contradicts a non-superseded,
|
|
31
|
+
non-DECLARED row, scopes overlapping → the DECLARED row alone becomes
|
|
32
|
+
`DIVERGES <id>[, ..]`; the ledger row is not touched, because our own wrong statement
|
|
33
|
+
puts nothing about the product in doubt. Two declarations that contradict each other
|
|
34
|
+
are `CONTESTED` — both are still held by their recipients.
|
|
35
|
+
|
|
27
36
|
The machine then verifies (the `graph` check): ids recompute, sources resolve and their
|
|
28
37
|
spans exist, `CONTESTED` is **symmetric** (editing one cell to `OK` fails the check —
|
|
29
38
|
the cheapest laundering), a `CONTESTED` pointer at a `SUPERSEDED` row is an error,
|
|
@@ -73,6 +82,9 @@ premise: signed amendments arrive later.
|
|
|
73
82
|
| newer source vs a ruling | new set {new row, ruling row}; escalation shows the `basis:` |
|
|
74
83
|
| member refined | the refining row inherits the member's contested relations (re-judged); counterparts rewritten to the new id |
|
|
75
84
|
| corroboration of a contested member | source appended; the set untouched — more evidence is not new information, and the escalation form shows source counts |
|
|
85
|
+
| a declaration contradicts the ledger | the DECLARED row → `DIVERGES <ledger ids>`; the ledger rows stay as they are; escalation names it |
|
|
86
|
+
| a new send corrects it | the new DECLARED row; the divergent one → `SUPERSEDED <new-id>` (a declaration is superseded only by a later declaration) |
|
|
87
|
+
| a ruling changes the decision | the ledger row → `SUPERSEDED <ruling-id>`; `check` then refuses the stale `DIVERGES` until the declaration is re-judged against the ruling: `OK` if it now agrees, `DIVERGES <ruling-id>` if not |
|
|
76
88
|
| bundled row split (`distillation.md` §3) | N atomic rows `OK`, each on its narrowed span; the bundle → `SUPERSEDED <id1>[, <id2> ..]`, text intact; contested relations re-judged per successor |
|
|
77
89
|
|
|
78
90
|
A superseded row keeps its `not-yet-honored-by` marker with its text. Copy the marker to each successor that still states behavior the code does not have; the analysis or shadow citing the predecessor's id satisfies the successor.
|
|
@@ -103,7 +115,11 @@ record like any claim-table edit.
|
|
|
103
115
|
Escalations are presented **once, at the end of a run** — the pipeline never stops
|
|
104
116
|
mid-ingest to ask. Each escalation names: the claims in the set, each one's source
|
|
105
117
|
(reopenable), date and provenance, and one line on why the machine cannot decide
|
|
106
|
-
("same subject, overlapping validity, GIVEN vs GIVEN, no newer source").
|
|
118
|
+
("same subject, overlapping validity, GIVEN vs GIVEN, no newer source"). A divergence
|
|
119
|
+
is escalated the same way, naming the recipient and the day it was sent: the
|
|
120
|
+
practitioner corrects it with a new send or changes the decision. One limit stated
|
|
121
|
+
out loud: `DIVERGES` is one-way, so unlike `CONTESTED` a hand edit back to `OK` has
|
|
122
|
+
no counterpart to fail on — the diff review is what catches it. **A question
|
|
107
123
|
that cannot fill this form is not askable** — it is the symptom that the answer is in
|
|
108
124
|
the corpus and was not searched. Generic confirmations are not questions.
|
|
109
125
|
|
|
@@ -122,10 +138,15 @@ An `IMPORTED` row is a ruling made by the owner of a different project
|
|
|
122
138
|
|
|
123
139
|
- It keeps its text, its span and its original `basis:` verbatim — the knowledge is not
|
|
124
140
|
lost, and relabelling it `DERIVED` would make the row lie about its origin.
|
|
125
|
-
-
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
- It rests on a renamed copy of its note (`imported_<project>_<name>`) that carries
|
|
142
|
+
`imported_from:`. Unnamed, the class says nothing and the row is a `RULING` with the
|
|
143
|
+
label filed off; the validator refuses it.
|
|
144
|
+
- **It cannot settle anything here.** `import` reopens the set it had settled: its former
|
|
145
|
+
members and the ruling arrive `CONTESTED` with each other. `SUPERSEDED <id>` pointing at
|
|
146
|
+
an `IMPORTED` row is an error: nobody in this project granted that decision its
|
|
147
|
+
authority.
|
|
148
|
+
|
|
149
|
+
Ratification is the ruling procedure above, and it is you deciding: your own note with
|
|
150
|
+
your own `basis:`, a `RULING` row on it, and every row `CONTESTED` with the imported one —
|
|
151
|
+
transitively, when it sits in more than one set — `SUPERSEDED <your id>`, the imported row
|
|
152
|
+
included. A `RULING` on a note that names a foreign origin is refused.
|
|
@@ -187,7 +187,10 @@ can block forever gets removed. **Log one row per completed review, PASS or FAIL
|
|
|
187
187
|
logging only passes would erase exactly that evidence. The row goes in
|
|
188
188
|
`ai_docs/audit/reviews/REVIEW_LOG.md` (create it if absent — `templates.md`):
|
|
189
189
|
`| date | doc_key | tier | model | reviewer | findings_raised | findings_real | verdict | revise_rounds |`,
|
|
190
|
-
with `tier` = `design` or `closure` in Standalone.
|
|
190
|
+
with `tier` = `design` or `closure` in Standalone. In the code lens, a wrong behaviour
|
|
191
|
+
the owner reports in a merged L3 change is one row too, tier `post-merge`, `model`
|
|
192
|
+
`none (owner report)`, reviewer `owner`, the owner quoted in the notes: a defect
|
|
193
|
+
record, not a review, so `benefit` keeps it out of every review figure. **`model` records the capability
|
|
191
194
|
tier that actually ran** — `deep`, `light`, `economy`, `single (client exposes no
|
|
192
195
|
choice)`, `below floor: <reason>`, or `none (not due)` on a NOT DUE row — never a provider name in doctrine (a project
|
|
193
196
|
may of course write its own model names in its own log). Without it the floor above
|
|
@@ -195,6 +198,34 @@ is a belief: the row would say a gate ran and never whether it could do its job,
|
|
|
195
198
|
the correlation with `revise_rounds` and `findings_real` — the only evidence the
|
|
196
199
|
policy pays — could not be drawn.
|
|
197
200
|
|
|
201
|
+
**The owner approves the design before the work starts (L3).** The reviewer judges
|
|
202
|
+
whether the design holds and serves the Vision; only the owner can say whether it is
|
|
203
|
+
what they meant beyond what the Vision wrote, and an intent error costs least before
|
|
204
|
+
the work exists. So the final design PASS — final once any scoped re-review it owed
|
|
205
|
+
has passed (§Review-driven corrections) — does not start the work: the agent
|
|
206
|
+
presents the design and asks the owner whether to proceed, in the form
|
|
207
|
+
`elicitation.md` §The form of a question gives a reserved approval. The presentation
|
|
208
|
+
is a short summary — what the change does (the Functional Spec, or the lens's
|
|
209
|
+
equivalent), for whom and through which surfaces (use cases, contract), what it
|
|
210
|
+
touches (Impact), the threats and their mitigations, the review's verdict with any
|
|
211
|
+
open findings — and the path of the full design. At the round cap, the hand-over the
|
|
212
|
+
cap prescribes, followed by this one question, takes the place of the form, and the
|
|
213
|
+
owner's answer settles both. An approval is recorded verbatim in the request record
|
|
214
|
+
under a label that begins `design approved`; a no or a correction takes its own
|
|
215
|
+
label, and the design goes back — where the revision changes what the review judged,
|
|
216
|
+
moment 1 runs again on that part first (the delta rule above). Only after the
|
|
217
|
+
approval does the ANALYSIS go `PLANNED` → `IN_PROGRESS` and the work start: code,
|
|
218
|
+
drafting or production, by lens. An instruction given before the design was shown
|
|
219
|
+
("carry on", "do the whole unit") does not approve it. Where a lens's own phases
|
|
220
|
+
already have the owner approve the design — the marketing lens's strategy USER GATE
|
|
221
|
+
— that gate is this approval, never a second question, and the lens's own unattended
|
|
222
|
+
path applies. Otherwise, unattended, the unit is `BLOCKED on the user`
|
|
223
|
+
(`elicitation.md` Unattended path), never approved by assumption; a late arrival
|
|
224
|
+
(moment 1b) asks before any further work. Hybrid: the owner's approval of the
|
|
225
|
+
`E-ISP`/`E-TDD` proposal in devPNT, under devPNT's own authorization rules, is this
|
|
226
|
+
approval. In Standalone, `validate` notes an L3 ANALYSIS in implementation, started
|
|
227
|
+
on or after 2026-10-31, whose request record holds no `design approved` entry.
|
|
228
|
+
|
|
198
229
|
**One schema for both modes**, meaning one required CORE plus each mode's own
|
|
199
230
|
**mode-specific** realization columns. The core is what both modes actually share: `date`, `doc_key`,
|
|
200
231
|
`tier`, `model`, `findings_raised`, `findings_real`, `verdict`, `revise_rounds`.
|
|
@@ -285,6 +316,11 @@ The binding inputs, by review type:
|
|
|
285
316
|
clause reads both), and the **probe harness** (`ai_docs/solutions/harness_[feature]/`).
|
|
286
317
|
Hand these *in addition to* the design artifact — the reviewer checks the artifact
|
|
287
318
|
**against** them, not only for internal consistency.
|
|
319
|
+
- **The constraint view comes last.** Where the ANALYSIS carries ids, the reviewer
|
|
320
|
+
derives the callers and behaviours first, then compares with
|
|
321
|
+
`sdlc_check.py constraints --unit <id>`: a constraint the design states and the
|
|
322
|
+
diff breaks is a finding. The view never replaces the ANALYSIS or the diff in the
|
|
323
|
+
request.
|
|
288
324
|
- **For a closure review, the diff and the test evidence**: the run output that
|
|
289
325
|
backs every completion claim, dated after the final relevant change.
|
|
290
326
|
- **For a design or closure review of an L3/E3 unit, the request record**
|
|
@@ -615,6 +615,11 @@ class Grounds:
|
|
|
615
615
|
return [{"kind": "untraceable", "path": rel}]
|
|
616
616
|
seen = seen | {rel}
|
|
617
617
|
if rel.startswith("corpus/given/"):
|
|
618
|
+
# F-084: a document this project SENT is our own declaration, not
|
|
619
|
+
# ground for a fact about the product.
|
|
620
|
+
side = self._meta(rel + ".meta.md")
|
|
621
|
+
if _v(side, "provenance").upper() == "DECLARED":
|
|
622
|
+
return [{"kind": "declared", "path": rel}]
|
|
618
623
|
return [{"kind": "document", "path": rel}]
|
|
619
624
|
if not _inside(self.docs, rel) or not (self.docs / rel).is_file():
|
|
620
625
|
return [{"kind": "untraceable", "path": rel}]
|
|
@@ -643,6 +648,8 @@ def ground_class(leaves):
|
|
|
643
648
|
return "statement"
|
|
644
649
|
if "untraceable" in kinds:
|
|
645
650
|
return "untraceable"
|
|
651
|
+
if "declared" in kinds:
|
|
652
|
+
return "declared"
|
|
646
653
|
if "kb" in kinds:
|
|
647
654
|
return "imported"
|
|
648
655
|
return "documents"
|
|
@@ -707,12 +714,15 @@ def build_kb(docs):
|
|
|
707
714
|
kind, rest = _kind(r)
|
|
708
715
|
f = {"text": r["claim"], "valid": r["valid"], "qty": r["qty"], "about": r["about"],
|
|
709
716
|
"origin": (r["prov"] or "").strip().lower(), "topic": slug,
|
|
710
|
-
"state": {"SUPERSEDED": "superseded", "CONTESTED": "contested"
|
|
717
|
+
"state": {"SUPERSEDED": "superseded", "CONTESTED": "contested",
|
|
718
|
+
"DIVERGES": "diverges"}.get(kind, "ok")}
|
|
711
719
|
if kind == "SUPERSEDED":
|
|
712
720
|
f["successors"] = _ids(rest)
|
|
713
721
|
f["chain_ends"] = chain_ends(r["id"], by_id)
|
|
714
722
|
if kind == "CONTESTED":
|
|
715
723
|
f["with"] = _ids(rest)
|
|
724
|
+
if kind == "DIVERGES":
|
|
725
|
+
f["diverges_from"] = _ids(rest)
|
|
716
726
|
srcs = []
|
|
717
727
|
for src in r["source"].split(";"):
|
|
718
728
|
src = src.strip()
|
|
@@ -1246,7 +1256,9 @@ def chronology(docs, pages):
|
|
|
1246
1256
|
text = _text(docs / rel) if (docs / rel).is_file() else None
|
|
1247
1257
|
if text is not None:
|
|
1248
1258
|
for row in sdlc_core.parse_review_log(text)[0]:
|
|
1249
|
-
|
|
1259
|
+
# F-085: a `post-merge` row is the owner's defect report, not a review
|
|
1260
|
+
kind = "defect" if row["tier"].lower().startswith("post-merge") else "review"
|
|
1261
|
+
add(row["date"], kind, "%s — %s: %s" % (
|
|
1250
1262
|
row["doc_key"], row["tier"], row["verdict"]), rel)
|
|
1251
1263
|
for p in sorted(docs.glob("architecture/ADR_*.md")):
|
|
1252
1264
|
rel = "architecture/" + p.name
|
|
@@ -1269,7 +1281,9 @@ def chronology(docs, pages):
|
|
|
1269
1281
|
continue
|
|
1270
1282
|
if kind == "source":
|
|
1271
1283
|
name = p.name[:-len(suffix)]
|
|
1272
|
-
|
|
1284
|
+
# F-084: a DECLARED sidecar's date is the day it was SENT
|
|
1285
|
+
sent = _v(fm, "provenance").upper() == "DECLARED"
|
|
1286
|
+
add(date, "sent" if sent else kind, name, rel, source=name)
|
|
1273
1287
|
else:
|
|
1274
1288
|
add(date, kind, title_of(rel, text), rel)
|
|
1275
1289
|
# Most recent first. Within one day a document's later entry is the more recent
|