@antoneeo/agentic-sdlc-skill 1.48.0 → 1.50.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.
@@ -126,8 +126,8 @@ Never legal:
126
126
 
127
127
  What questions are FOR — what the user uniquely owns: the benefit, priorities
128
128
  between conflicting goods, non-goals, acceptance, and the approvals doctrine
129
- reserves to them (Vision promotion and amendment, scope changes, proposal
130
- acceptance, merge decisions). Facts about intent come from the user; facts about
129
+ reserves to them (Vision promotion and amendment, design approval at L3, scope
130
+ changes, proposal acceptance, merge decisions). Facts about intent come from the user; facts about
131
131
  the system come from search.
132
132
 
133
133
  **Precedence, because both halves can fire at once:** this paragraph wins over
@@ -262,6 +262,12 @@ in the snapshot, never to your assumption about what the code does.
262
262
  (source-anchored), not an elaborated version of it.
263
263
  - When in doubt about whether something is "supported" by the source, treat it
264
264
  as not covered rather than stretching the marker to fit.
265
+ - **Declared exception: `## Preservation constraints`.** A code guide may carry
266
+ this section, copied at an L3 closure from the ANALYSIS's table (code lens,
267
+ `SKILL.md` Phase 5). The section's marker cites each row it holds as
268
+ `[source: solutions/ANALYSIS_<name>.md#P<n>]` instead of a snapshot anchor: each
269
+ row records what the code must keep doing, as that unit enumerated and probed it,
270
+ not an excerpt of the code.
265
271
 
266
272
  ## 4. Ingestion bound (T9)
267
273
 
@@ -293,5 +299,7 @@ state it explicitly when handing off a newly created guide.
293
299
  closure (docs travel with the code); when you merely READ one, check it against
294
300
  the code before trusting it. Too large to absorb → mark it `SUPERSEDED` rather
295
301
  than leave a confident-wrong map badged CURRENT, which is worse than none.
302
+ - **A regenerated guide keeps `## Preservation constraints` unchanged**: the
303
+ section does not come from the snapshot (§3), so regeneration carries it over.
296
304
  - **Agent-global KB guides** use the same pipeline and validator via
297
305
  `--root ~/.agentic-sdlc`; freshness via the same `stale` engine.
@@ -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. **`model` records the capability
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"}.get(kind, "ok")}
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
- add(row["date"], "review", "%s — %s: %s" % (
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
- add(date, kind, name, rel, source=name)
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