guardian-framework 0.1.163 → 0.1.165
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/dist/cli.js
CHANGED
|
@@ -3206,7 +3206,7 @@ __export(exports_package, {
|
|
|
3206
3206
|
bin: () => bin,
|
|
3207
3207
|
author: () => author
|
|
3208
3208
|
});
|
|
3209
|
-
var name = "guardian-framework", version = "0.1.
|
|
3209
|
+
var name = "guardian-framework", version = "0.1.165", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
3210
3210
|
var init_package = __esm(() => {
|
|
3211
3211
|
exports = {
|
|
3212
3212
|
".": {
|
package/dist/exports.js
CHANGED
|
@@ -1991,7 +1991,7 @@ __export(exports_package, {
|
|
|
1991
1991
|
bin: () => bin,
|
|
1992
1992
|
author: () => author
|
|
1993
1993
|
});
|
|
1994
|
-
var name = "guardian-framework", version = "0.1.
|
|
1994
|
+
var name = "guardian-framework", version = "0.1.165", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
1995
1995
|
var init_package = __esm(() => {
|
|
1996
1996
|
exports = {
|
|
1997
1997
|
".": {
|
package/package.json
CHANGED
|
@@ -204,6 +204,16 @@ export function generateEpicTestFiles(options: TddGenerateOptions): string[] {
|
|
|
204
204
|
|
|
205
205
|
mkdirSync(testDir, { recursive: true });
|
|
206
206
|
|
|
207
|
+
// Python: a unique __init__.py per test package. Without it pytest's
|
|
208
|
+
// default "prepend" import mode imports test modules by BASENAME, so
|
|
209
|
+
// <module>/domain-services/test_domain-services.py collides with
|
|
210
|
+
// <other-module>/domain-services/test_domain-services.py and the whole
|
|
211
|
+
// suite fails to collect once two modules share a component name.
|
|
212
|
+
if (language === "python") {
|
|
213
|
+
const initPath = join(testDir, "__init__.py");
|
|
214
|
+
if (!existsSync(initPath)) writeFileSync(initPath, "", "utf-8");
|
|
215
|
+
}
|
|
216
|
+
|
|
207
217
|
const fileName = getTestFileName(componentId, language);
|
|
208
218
|
const filePath = join(testDir, fileName);
|
|
209
219
|
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
# freeze.sh status [--root DIR]
|
|
17
17
|
# freeze.sh list [--root DIR]
|
|
18
18
|
# freeze.sh next [--root DIR] # next module eligible to freeze, in order
|
|
19
|
+
# freeze.sh unfreeze --module <m> --reason "<why>" [--root DIR]
|
|
19
20
|
#
|
|
20
21
|
# Exit: 0 pass · 1 verification failed · 2 usage/state error
|
|
21
22
|
# ============================================================================
|
|
22
23
|
set -uo pipefail
|
|
23
24
|
|
|
24
25
|
CMD="${1:-}"; shift || true
|
|
25
|
-
MODULE=""; ROOT_OPT=""; BASE="main"; JSON_OUT=0; COMMIT=""
|
|
26
|
+
MODULE=""; ROOT_OPT=""; BASE="main"; JSON_OUT=0; COMMIT=""; REASON=""
|
|
26
27
|
|
|
27
28
|
while [ $# -gt 0 ]; do
|
|
28
29
|
case "$1" in
|
|
@@ -31,6 +32,7 @@ while [ $# -gt 0 ]; do
|
|
|
31
32
|
--base) BASE="${2:-main}"; shift 2 ;;
|
|
32
33
|
--commit) COMMIT="${2:-}"; shift 2 ;;
|
|
33
34
|
--json) JSON_OUT=1; shift ;;
|
|
35
|
+
--reason) REASON="${2:-}"; shift 2 ;;
|
|
34
36
|
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
35
37
|
esac
|
|
36
38
|
done
|
|
@@ -54,15 +56,15 @@ usage() {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
case "$CMD" in
|
|
57
|
-
verify | record | status | list | next) ;;
|
|
59
|
+
verify | record | status | list | next | unfreeze) ;;
|
|
58
60
|
*) usage; exit 2 ;;
|
|
59
61
|
esac
|
|
60
62
|
|
|
61
|
-
exec python3 - "$CMD" "$ROOT" "$GIT_TOP" "$STATE" "$MODULE" "$BASE" "$JSON_OUT" "$COMMIT" <<'PY'
|
|
63
|
+
exec python3 - "$CMD" "$ROOT" "$GIT_TOP" "$STATE" "$MODULE" "$BASE" "$JSON_OUT" "$COMMIT" "$REASON" <<'PY'
|
|
62
64
|
import json, os, re, subprocess, sys
|
|
63
65
|
from datetime import datetime, timezone
|
|
64
66
|
|
|
65
|
-
cmd, root, git_top, state_path, module, base, json_out, commit = sys.argv[1:
|
|
67
|
+
cmd, root, git_top, state_path, module, base, json_out, commit, reason = sys.argv[1:10]
|
|
66
68
|
json_out = json_out == "1"
|
|
67
69
|
|
|
68
70
|
MODS = os.path.join(root, ".pi", "architecture", "modules")
|
|
@@ -232,6 +234,23 @@ def classify(code):
|
|
|
232
234
|
|
|
233
235
|
AC_ID = re.compile(r"\b[A-Z]{1,3}-\d{1,3}\b")
|
|
234
236
|
|
|
237
|
+
def find_all_test_basenames():
|
|
238
|
+
"""basename -> [paths] for every test file in the project."""
|
|
239
|
+
out = {}
|
|
240
|
+
for base_dir in ("tests",):
|
|
241
|
+
base_path = os.path.join(root, base_dir)
|
|
242
|
+
if not os.path.isdir(base_path):
|
|
243
|
+
continue
|
|
244
|
+
for dp, _dn, fs in os.walk(base_path):
|
|
245
|
+
if "__pycache__" in dp:
|
|
246
|
+
continue
|
|
247
|
+
for f in fs:
|
|
248
|
+
stem = f.rsplit(".", 1)[0]
|
|
249
|
+
if stem.startswith("test_") or stem.endswith("_test"):
|
|
250
|
+
out.setdefault(f, []).append(os.path.join(dp, f))
|
|
251
|
+
return out
|
|
252
|
+
|
|
253
|
+
|
|
235
254
|
def find_tests(slug):
|
|
236
255
|
"""Test files for the module. `tests/` wins; `src/` is a fallback only for
|
|
237
256
|
projects that colocate tests, so source files are never counted as suites."""
|
|
@@ -391,6 +410,50 @@ def verify(slug):
|
|
|
391
410
|
checks.append(("red state", ok_red, f"{status}: {reason}"))
|
|
392
411
|
ok_all &= ok_red
|
|
393
412
|
|
|
413
|
+
# 3b. static types — a frozen contract that does not type-check is a defect.
|
|
414
|
+
# This is what caught `Invoice.record` shadowing `AggregateRoot.record`
|
|
415
|
+
# (a classmethod factory hiding the event recorder) inside a frozen module.
|
|
416
|
+
pyproject = read(os.path.join(root, "pyproject.toml"))
|
|
417
|
+
has_mypy_cfg = ("[tool.mypy]" in pyproject
|
|
418
|
+
or os.path.exists(os.path.join(root, "mypy.ini"))
|
|
419
|
+
or "[mypy]" in read(os.path.join(root, "setup.cfg")))
|
|
420
|
+
mypy = os.path.join(root, ".venv", "bin", "mypy")
|
|
421
|
+
# dedupe: for a slug without hyphens both candidates are the same path, and
|
|
422
|
+
# passing it twice makes mypy report "Duplicate module named …"
|
|
423
|
+
pkg_dirs = sorted({d for d in (os.path.join(root, "src", slug),
|
|
424
|
+
os.path.join(root, "src", slug.replace("-", "_")))
|
|
425
|
+
if os.path.isdir(d)})
|
|
426
|
+
if has_mypy_cfg and pkg_dirs and (os.path.exists(mypy) or os.path.exists("/usr/bin/env")):
|
|
427
|
+
try:
|
|
428
|
+
tc = subprocess.run([mypy if os.path.exists(mypy) else "mypy", *pkg_dirs],
|
|
429
|
+
cwd=root, capture_output=True, text=True, timeout=600)
|
|
430
|
+
ok_tc = tc.returncode == 0
|
|
431
|
+
tail = [l for l in (tc.stdout or "").splitlines() if ": error:" in l][:3]
|
|
432
|
+
checks.append(("type check", ok_tc,
|
|
433
|
+
"mypy clean" if ok_tc else "mypy errors: " + " | ".join(tail)))
|
|
434
|
+
ok_all &= ok_tc
|
|
435
|
+
except Exception as e:
|
|
436
|
+
checks.append(("type check", True, f"skipped ({e})"))
|
|
437
|
+
|
|
438
|
+
# 3c. test isolation: duplicate test basenames across packages break pytest
|
|
439
|
+
# collection entirely (prepend import mode imports by basename), which
|
|
440
|
+
# silently hides every suite behind "2 errors during collection".
|
|
441
|
+
if tests:
|
|
442
|
+
by_base = {}
|
|
443
|
+
for t in tests:
|
|
444
|
+
by_base.setdefault(os.path.basename(t), []).append(t)
|
|
445
|
+
for base, paths in find_all_test_basenames().items():
|
|
446
|
+
if len(paths) < 2:
|
|
447
|
+
continue
|
|
448
|
+
dirs = {os.path.dirname(p) for p in paths}
|
|
449
|
+
isolated = all(os.path.exists(os.path.join(d, "__init__.py")) for d in dirs)
|
|
450
|
+
if not isolated:
|
|
451
|
+
checks.append(("test isolation", False,
|
|
452
|
+
f"'{base}' exists in {len(dirs)} packages without __init__.py — "
|
|
453
|
+
f"pytest collection collides; add __init__.py or set import-mode=importlib"))
|
|
454
|
+
ok_all = False
|
|
455
|
+
break
|
|
456
|
+
|
|
394
457
|
# 4b. recorded evidence must still hold (spec: a contract change means
|
|
395
458
|
# the module is no longer frozen and must be re-frozen).
|
|
396
459
|
st_rec = load_state()
|
|
@@ -519,6 +582,31 @@ if cmd == "next":
|
|
|
519
582
|
print("no eligible module: every module is frozen/implemented or blocked by an unfrozen dependency")
|
|
520
583
|
raise SystemExit(1)
|
|
521
584
|
|
|
585
|
+
if cmd == "unfreeze":
|
|
586
|
+
if not module:
|
|
587
|
+
print("usage: freeze.sh unfreeze --module <m> --reason \"why\"", file=sys.stderr)
|
|
588
|
+
raise SystemExit(2)
|
|
589
|
+
if not reason:
|
|
590
|
+
print("refusing to unfreeze without a reason: a contract change must be recorded",
|
|
591
|
+
file=sys.stderr)
|
|
592
|
+
raise SystemExit(2)
|
|
593
|
+
slug = slugify(module)
|
|
594
|
+
st = load_state()
|
|
595
|
+
e = entry(st, slug)
|
|
596
|
+
if not e:
|
|
597
|
+
print(f"{slug} is not recorded — nothing to unfreeze", file=sys.stderr)
|
|
598
|
+
raise SystemExit(2)
|
|
599
|
+
history = e.get("history") or []
|
|
600
|
+
if e.get("evidence"):
|
|
601
|
+
history.append({"evidence": e["evidence"], "state": e.get("state"),
|
|
602
|
+
"unfrozenAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
603
|
+
"reason": reason})
|
|
604
|
+
set_entry(st, slug, state="unfrozen", contractFrozen=False, evidence=None, history=history)
|
|
605
|
+
save_state(st)
|
|
606
|
+
print(f"unfrozen: {slug} — {reason}")
|
|
607
|
+
print("Re-run Phase A for the changed contract, then record again.")
|
|
608
|
+
raise SystemExit(0)
|
|
609
|
+
|
|
522
610
|
# ── commands ────────────────────────────────────────────────────────────────
|
|
523
611
|
if cmd == "list":
|
|
524
612
|
st = load_state()
|