@softspark/ai-toolkit 2.9.0 → 2.10.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.
@@ -147,7 +147,7 @@ def generate_general_guidelines() -> str:
147
147
  lines = [
148
148
  "## General Guidelines",
149
149
  "",
150
- '- Apply "Safety First": no data loss, no blind execution, max 3 loop iterations',
150
+ '- Apply "Safety First": no data loss, no blind execution, max 5 loop iterations',
151
151
  "- Research before acting: check existing code and context before proposing changes",
152
152
  "- Use structured commits: feat/fix/docs/refactor/test/chore prefixes",
153
153
  "- Quality gates: lint must pass, types must check, tests must be green before done",
@@ -162,7 +162,7 @@ def generate_quality_standards() -> str:
162
162
  lines = [
163
163
  "## Quality Standards",
164
164
  "",
165
- "Derived from the immutable safety constitution (5 articles):",
165
+ "Derived from the immutable safety constitution (6 articles):",
166
166
  "",
167
167
  "**Article I — Safety First**",
168
168
  "- No data loss: never delete files without backup verification"
@@ -170,7 +170,7 @@ def generate_quality_standards() -> str:
170
170
  "- No blind execution: never run LLM-generated code without"
171
171
  " static analysis or review",
172
172
  "- No infinite loops: all autonomous loops must have a maximum"
173
- " iteration count (max 3)",
173
+ " iteration count (max 5)",
174
174
  "",
175
175
  "**Article II — Hierarchy of Truth**",
176
176
  "- The Knowledge Base (`kb/`) is the source of truth;"
@@ -196,6 +196,21 @@ def generate_quality_standards() -> str:
196
196
  " user confirmation",
197
197
  "- Operate within assigned model tiers; model tier changes"
198
198
  " require user approval",
199
+ "",
200
+ "**Article VI — Repair Discipline**",
201
+ "- No dead code: unused code (files, classes, functions, imports,"
202
+ " variables) must be removed in the same change that makes it"
203
+ " unused; 'pre-existing' or 'out of scope' is not a valid reason",
204
+ "- Fix every found bug: bugs, gaps, missing tests, or stale docs"
205
+ " discovered during a task must be fixed in the same change when"
206
+ " directly adjacent to the work; deferral requires explicit user"
207
+ " decision",
208
+ "- Tests and docs follow behavior: behavior changes must carry"
209
+ " matching integration and unit tests plus affected documentation"
210
+ " in the same change",
211
+ "- Verify before claiming done: re-read the diff before marking"
212
+ " a task complete; no orphaned references, no missing coverage,"
213
+ " no stale docs",
199
214
  ]
200
215
  return "\n".join(lines)
201
216
 
@@ -233,7 +248,7 @@ def generate_quality_guidelines() -> str:
233
248
  "## Quality Guidelines",
234
249
  "",
235
250
  '- **Safety First**: No data loss, no blind execution,'
236
- " maximum 3 autonomous loop iterations",
251
+ " maximum 5 autonomous loop iterations",
237
252
  "- **No Blind Execution**: Never run LLM-generated code"
238
253
  " without static analysis or review",
239
254
  '- **Tests are Sacred**: "Green Tests" is the only definition of Done;'
@@ -81,7 +81,7 @@
81
81
  "constitution": {
82
82
  "type": "object",
83
83
  "additionalProperties": false,
84
- "description": "Constitution amendments. Articles I-V are immutable. Base articles are immutable. Projects can only ADD new articles.",
84
+ "description": "Constitution amendments. Articles I-VI are immutable. Base articles are immutable. Projects can only ADD new articles (7+).",
85
85
  "properties": {
86
86
  "amendments": {
87
87
  "type": "array",
@@ -664,6 +664,66 @@ def _validate_version_sync(tk_dir: Path, vr: ValidationResult) -> None:
664
664
  )
665
665
 
666
666
 
667
+ ROMAN_NUMERALS = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"]
668
+
669
+
670
+ def validate_constitution_drift(tk_dir: Path, vr: ValidationResult) -> None:
671
+ """Detect article-count drift between constitution.md and downstream docs."""
672
+ print()
673
+ print("## Constitution Drift")
674
+
675
+ constitution = tk_dir / "app" / "constitution.md"
676
+ if not constitution.is_file():
677
+ return
678
+
679
+ content = constitution.read_text(encoding="utf-8")
680
+ matches = re.findall(r"^## Article ([IVX]+):", content, re.MULTILINE)
681
+ if not matches:
682
+ vr.error("Constitution has no '## Article <roman>:' headings")
683
+ return
684
+
685
+ count = len(matches)
686
+ if count > len(ROMAN_NUMERALS):
687
+ vr.error(f"Constitution has {count} articles (more than {len(ROMAN_NUMERALS)} supported)")
688
+ return
689
+ max_roman = ROMAN_NUMERALS[count - 1]
690
+
691
+ docs = [
692
+ tk_dir / "README.md",
693
+ tk_dir / "app" / "ARCHITECTURE.md",
694
+ tk_dir / "kb" / "reference" / "architecture-overview.md",
695
+ tk_dir / "kb" / "reference" / "enterprise-config-guide.md",
696
+ ]
697
+
698
+ count_pat = re.compile(r"\b(\d+)\s+(?:immutable\s+safety\s+)?articles?\b", re.IGNORECASE)
699
+ range_pat = re.compile(r"\bArticles?\s+I-([IVX]+)\b")
700
+ drift = 0
701
+ for doc in docs:
702
+ if not doc.is_file():
703
+ continue
704
+ text = doc.read_text(encoding="utf-8")
705
+ for m in count_pat.finditer(text):
706
+ n = int(m.group(1))
707
+ if 1 <= n <= len(ROMAN_NUMERALS) and n != count:
708
+ vr.error(
709
+ f"{doc.relative_to(tk_dir)} references '{n} articles' "
710
+ f"but constitution has {count}"
711
+ )
712
+ drift += 1
713
+ for m in range_pat.finditer(text):
714
+ end = m.group(1).upper()
715
+ if end != max_roman and end in ROMAN_NUMERALS:
716
+ vr.error(
717
+ f"{doc.relative_to(tk_dir)} references 'Articles I-{end}' "
718
+ f"but constitution has I-{max_roman}"
719
+ )
720
+ drift += 1
721
+
722
+ if drift == 0:
723
+ print(f" OK: constitution has {count} articles (I-{max_roman}), docs consistent")
724
+ print()
725
+
726
+
667
727
  def validate_content_quality(tk_dir: Path, vr: ValidationResult) -> None:
668
728
  """Check content quality: name matches directory, non-empty body."""
669
729
  print()
@@ -709,6 +769,7 @@ def _run_all_checks(tk_dir: Path, vr: ValidationResult) -> tuple[int, int, str]:
709
769
  validate_kb_documents(tk_dir, vr)
710
770
  validate_core_files(tk_dir, vr)
711
771
  actual_tests = validate_metadata_contracts(tk_dir, agent_count, skill_count, vr)
772
+ validate_constitution_drift(tk_dir, vr)
712
773
  validate_content_quality(tk_dir, vr)
713
774
  return agent_count, skill_count, actual_tests
714
775