@appchy/jarvis 0.1.76 → 0.1.77

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/bin.js CHANGED
@@ -10109,7 +10109,7 @@ import { createRequire as createRequire2 } from "module";
10109
10109
  var _require = createRequire2(import.meta.url);
10110
10110
  var VERSION2 = _require("../package.json").version ?? "0.0.0";
10111
10111
  var IS_DEV = String(_require("../package.json").name ?? "").endsWith("-dev");
10112
- var SHA = "7a9afe2";
10112
+ var SHA = "f6d3152";
10113
10113
  var BUILT = "2026-09-10";
10114
10114
  var BUILD = SHA ?? "source";
10115
10115
  var BUILD_LABEL = `${SHA ? `${VERSION2} (${SHA}${BUILT ? ` ${BUILT}` : ""})` : `${VERSION2} (source)`}${IS_DEV ? " \u2014 development build" : ""}`;
@@ -566,7 +566,7 @@ def _align_acceptance(root: Path) -> list:
566
566
  # only test never executed, which is the false green the whole join exists to
567
567
  # catch. Only statuses the run explicitly reports as not-passed are subtracted,
568
568
  # so `align` still works before anyone has produced a run.
569
- run, _, _ = _load_run(repo)
569
+ run = _load_run(repo).proven
570
570
  unproven = {cid for cid, status in run.items() if status in ("todo", "failed")}
571
571
 
572
572
  for name, md in sorted(features.items()):
@@ -70,9 +70,10 @@ def cmd_coverage(args) -> int:
70
70
  nobody reads. The flip is its own task."""
71
71
  root = find_work_root()
72
72
  repo = root.parent
73
- run, runners, shards = _load_run(repo)
73
+ read = _load_run(repo)
74
+ run, runners, shards = read.proven, read.runners, read.shards
74
75
 
75
- if not shards:
76
+ if not shards and not read.stale:
76
77
  # The commands come from `verify.*` in the repo's config. They used to be
77
78
  # one repo's own package-manager invocations, hardcoded — which told every
78
79
  # other consumer to run a command it does not have. A name-only literal
@@ -142,7 +143,21 @@ def cmd_coverage(args) -> int:
142
143
  len(built & todo), len(declared - built),
143
144
  sorted(failed), sorted(gap), sorted(ahead)))
144
145
 
145
- print(f"\n read {len(shards)} shard(s): {', '.join(shards)}\n")
146
+ print(f"\n read {len(shards)} shard(s): {', '.join(shards)}")
147
+ # WHAT WAS SET ASIDE, on the same screen as what was counted. A result whose
148
+ # test file is gone and a result that never existed look identical in the
149
+ # totals, and telling them apart is the difference between "re-run that suite"
150
+ # and "nobody has ever proved this".
151
+ if read.stale:
152
+ folders = sorted({s.rsplit("/", 1)[0] for s in read.stale if "/" in s})
153
+ print(f" ignored {len(read.stale)} shard(s) whose test file is gone"
154
+ f"{' (' + ', '.join(folders) + ')' if folders else ''}"
155
+ " — re-run those suites to replace them")
156
+ if read.unattributed:
157
+ print(f" {len(read.unattributed)} shard(s) do not say which file they ran"
158
+ " — counted, but nothing can check them: "
159
+ + ", ".join(read.unattributed))
160
+ print()
146
161
  print(f" {'feature':<18} {'built':>6} {'PROVEN':>7} {'eyes-on':>8} {'todo':>5} "
147
162
  f"{'gap':>4} {'unbuilt':>8} status")
148
163
  print(f" {'-' * 18} {'-' * 6} {'-' * 7} {'-' * 8} {'-' * 5} {'-' * 4} "
@@ -577,7 +577,7 @@ def gate(root, task, accept: str = "", owner: str = "") -> list:
577
577
  # a recorded sentence saying what a human saw. A `covers:` with neither is the
578
578
  # exact shape of "done on the model's say-so".
579
579
  if task.covers:
580
- proven, _, _ = _load_run(root.parent)
580
+ proven = _load_run(root.parent).proven
581
581
  seen = _observed_acs(task)
582
582
  feature = (task.owner or "").split("/")[-1]
583
583
  for ac in task.covers:
@@ -1,8 +1,8 @@
1
- """Reading the coverage shards — the ONE reader, as `sdk/testing/shard.ts` is the one writer.
1
+ """Reading the coverage shards — the ONE reader, as the covers package is the one writer.
2
2
 
3
- Every runner (vitest, Playwright, the `examples/*.mjs` smokes) drops a
4
- `.work/coverage/<runner>-<workspace>.json` carrying what that run actually proved. Three
5
- checks need to read them — `coverage` reports them, `align` refuses to call a skipped
3
+ Every runner (vitest, Playwright, doctest, a hand-rolled script) drops a
4
+ `.work/coverage/<runner>-<workspace>-<source>.json` carrying what that run actually proved.
5
+ Three checks need to read them — `coverage` reports them, `align` refuses to call a skipped
6
6
  binding evidence, and `lint`'s rollup asks whether a shipped feature has any. Each growing
7
7
  its own reader is the drift the shard format exists to prevent, and it also produced a real
8
8
  import cycle the moment the second one wanted it.
@@ -11,6 +11,7 @@ import cycle the moment the second one wanted it.
11
11
  """
12
12
  import json
13
13
  from pathlib import Path
14
+ from typing import NamedTuple
14
15
 
15
16
 
16
17
  #: Precedence when several sites claim one criterion — the SAME rank both
@@ -20,9 +21,30 @@ from pathlib import Path
20
21
  _RANK = {"failed": 3, "todo": 2, "passed": 1}
21
22
 
22
23
 
23
- def _load_run(repo: Path) -> tuple[dict, dict, list]:
24
- """`{'feature/AC-nn': status}` from every shard, the RUNNERS that claimed each
25
- criterion, and the shard names read.
24
+ class Run(NamedTuple):
25
+ """What the shards on disk add up to, and what had to be set aside to get there.
26
+
27
+ `stale` and `unattributed` are as much a part of the answer as `proven`: a
28
+ reader who cannot see that evidence was discarded cannot tell it from evidence
29
+ that never existed, and that is the failure this whole module guards.
30
+ """
31
+
32
+ #: `{'feature/AC-nn': 'passed' | 'failed' | 'todo'}`.
33
+ proven: dict
34
+ #: `{'feature/AC-nn': {runner, …}}` — what KIND of test claimed each one.
35
+ runners: dict
36
+ #: The shard filenames actually read.
37
+ shards: list
38
+ #: Source paths named by a shard that is no longer in the tree. Their claims
39
+ #: are not in `proven`.
40
+ stale: list
41
+ #: Shards that never say what they ran. Their claims ARE in `proven` — there
42
+ #: is nothing to check them against — and they are named so a reader knows it.
43
+ unattributed: list
44
+
45
+
46
+ def _load_run(repo: Path) -> Run:
47
+ """Every shard's verdict on every criterion, merged.
26
48
 
27
49
  The merge applies `failed > todo > passed` ACROSS shards, because a promise
28
50
  is one behaviour however many runners touch it: proven here and failing
@@ -34,30 +56,45 @@ def _load_run(repo: Path) -> tuple[dict, dict, list]:
34
56
  `playwright.json`. A criterion bound at both levels reported whichever
35
57
  runner sorted last, which is the one thing this file exists not to do.
36
58
 
59
+ A SHARD OUTLIVES THE TEST THAT WROTE IT, so one whose `source` is no longer
60
+ in the tree is set aside rather than read. It is a result about code this
61
+ checkout does not have, and nothing rewrites it: a renamed or deleted spec
62
+ would otherwise go on claiming `passed` forever, which is the same fault a
63
+ `// Covers:` comment is banned for. The merge above saves only the
64
+ contradicted case — a criterion with a stale pass AND a live failure reads
65
+ as failing — and does nothing at all for one whose only record is stale.
66
+
37
67
  The runners are kept because they say what KIND of test claimed a criterion:
38
68
  a browser suite proves what is on screen, a unit runner proves a value. Which
39
69
  vitest suite is `unit` and which is `integration` is not derivable here, and is
40
70
  not guessed at — see `AC_LEVELS`. `_wrong_level` is the only caller that needs
41
- them, and it is why this returns three values rather than two: a second reader
42
- that tracked runners separately is exactly the drift this module ended.
71
+ them, and it is why this returns them at all: a second reader that tracked
72
+ runners separately is exactly the drift this module ended.
43
73
  """
44
- out: dict = {}
45
- runners: dict = {}
46
- shards: list = []
74
+ read = Run({}, {}, [], [], [])
47
75
  d = repo / ".work" / "coverage"
48
76
  if not d.is_dir():
49
- return out, runners, shards
77
+ return read
50
78
  for p in sorted(d.glob("*.json")):
51
79
  try:
52
80
  data = json.loads(p.read_text())
53
81
  except Exception:
54
82
  continue
55
- shards.append(p.name)
83
+ source = data.get("source")
84
+ if not isinstance(source, str) or source == "":
85
+ # COUNTED, AND NAMED. A runner that does not write a source cannot be
86
+ # checked, and dropping it would throw away real evidence to punish a
87
+ # writer. Saying so is what lets somebody fix the writer.
88
+ read.unattributed.append(p.name)
89
+ elif not (repo / source).exists():
90
+ read.stale.append(source)
91
+ continue
92
+ read.shards.append(p.name)
56
93
  runner = data.get("runner") or "unknown"
57
94
  for cid, rec in (data.get("covered") or {}).items():
58
95
  status = rec.get("status") if isinstance(rec, dict) else str(rec)
59
- runners.setdefault(cid, set()).add(runner)
60
- prior = out.get(cid)
96
+ read.runners.setdefault(cid, set()).add(runner)
97
+ prior = read.proven.get(cid)
61
98
  if prior is None or _RANK.get(status, 0) > _RANK.get(prior, 0):
62
- out[cid] = status
63
- return out, runners, shards
99
+ read.proven[cid] = status
100
+ return read
@@ -1008,7 +1008,10 @@ def test_epic_covers_must_be_feature_qualified():
1008
1008
 
1009
1009
  def _shards(tmp: str, **byname: dict) -> Path:
1010
1010
  """A repo with `.work/coverage/` holding one shard per keyword — the file
1011
- NAME matters, since the merge used to depend on the order they sort in."""
1011
+ NAME matters, since the merge used to depend on the order they sort in.
1012
+
1013
+ A `source` key is written only where a test asks for one, so the merge tests
1014
+ above stay about the merge."""
1012
1015
  d = Path(tmp) / ".work" / "coverage"
1013
1016
  d.mkdir(parents=True)
1014
1017
  for name, covered in byname.items():
@@ -1016,6 +1019,16 @@ def _shards(tmp: str, **byname: dict) -> Path:
1016
1019
  return Path(tmp)
1017
1020
 
1018
1021
 
1022
+ def _ran(repo: Path, name: str, source: str, covered: dict) -> Path:
1023
+ """One shard that names the file it ran. Returns that file's path so a test
1024
+ can create it, or rename it away."""
1025
+ d = repo / ".work" / "coverage"
1026
+ d.mkdir(parents=True, exist_ok=True)
1027
+ (d / f"{name}.json").write_text(
1028
+ json.dumps({"runner": "vitest", "source": source, "covered": covered}))
1029
+ return repo / source
1030
+
1031
+
1019
1032
  def test_a_todo_in_one_shard_beats_a_pass_in_another():
1020
1033
  # The masking bug: a criterion driven at both levels — a passing vitest
1021
1034
  # suite AND a Playwright `test.fixme` — is HALF covered, and half is not
@@ -1029,9 +1042,9 @@ def test_a_todo_in_one_shard_beats_a_pass_in_another():
1029
1042
  # Three values since the level model landed: the merged statuses, the
1030
1043
  # RUNNERS that claimed each id, and the shards read. This call site was
1031
1044
  # left unpacking two and nothing caught it — `scripts/` is in no gate.
1032
- run, _runners, shards = coverage._load_run(repo)
1033
- assert run["courses/AC-01"] == "todo", run
1034
- assert len(shards) == 2
1045
+ read = coverage._load_run(repo)
1046
+ assert read.proven["courses/AC-01"] == "todo", read.proven
1047
+ assert len(read.shards) == 2
1035
1048
 
1036
1049
 
1037
1050
  def test_a_failure_anywhere_outranks_everything():
@@ -1066,6 +1079,59 @@ def test_a_covers_with_no_tests_never_promotes_a_criterion():
1066
1079
  assert coverage._load_run(repo)[0]["x/AC-01"] == "passed"
1067
1080
 
1068
1081
 
1082
+ def test_a_shard_whose_test_file_is_gone_proves_nothing():
1083
+ # A RENAME, made deliberately, on a fixture — never on this tree's history.
1084
+ # The shard is untouched by the rename and goes on saying `passed`; nothing
1085
+ # will ever rewrite it, because the run that would is gone with the file.
1086
+ with tempfile.TemporaryDirectory() as tmp:
1087
+ repo = Path(tmp)
1088
+ was = _ran(repo, "vitest-picker", "tests/picker.test.ts",
1089
+ {"projects/AC-01": {"status": "passed"}})
1090
+ was.parent.mkdir(parents=True, exist_ok=True)
1091
+ was.write_text("")
1092
+ assert coverage._load_run(repo).proven["projects/AC-01"] == "passed"
1093
+
1094
+ was.rename(was.parent / "chooser.test.ts")
1095
+ read = coverage._load_run(repo)
1096
+ assert "projects/AC-01" not in read.proven, read.proven
1097
+ assert read.shards == []
1098
+
1099
+
1100
+ def test_what_was_set_aside_is_reported_rather_than_dropped():
1101
+ # A reader who cannot see that evidence was discarded cannot tell it from
1102
+ # evidence that never existed, and would go looking for a test to write
1103
+ # rather than a suite to re-run.
1104
+ with tempfile.TemporaryDirectory() as tmp:
1105
+ repo = _shards(tmp, doctest={"engine/AC-02": {"status": "passed"}})
1106
+ _ran(repo, "vitest-picker", "tests/picker.test.ts",
1107
+ {"projects/AC-01": {"status": "passed"}})
1108
+ read = coverage._load_run(repo)
1109
+
1110
+ assert read.stale == ["tests/picker.test.ts"]
1111
+ # The one with no source at all is COUNTED — there is nothing to check it
1112
+ # against, and dropping it would throw away real evidence to punish a
1113
+ # writer — but it is named, so the writer can be fixed.
1114
+ assert read.unattributed == ["doctest.json"]
1115
+ assert read.proven["engine/AC-02"] == "passed"
1116
+
1117
+
1118
+ def test_a_live_failure_is_not_argued_away_by_a_stale_pass():
1119
+ # The merge already handled this one, and it is the ONLY case it handled: a
1120
+ # criterion whose sole record is a stale pass had nothing to contradict it.
1121
+ with tempfile.TemporaryDirectory() as tmp:
1122
+ repo = Path(tmp)
1123
+ live = _ran(repo, "playwright-bar", "e2e/bar.spec.ts",
1124
+ {"daw/AC-16": {"status": "failed"}})
1125
+ live.parent.mkdir(parents=True, exist_ok=True)
1126
+ live.write_text("")
1127
+ _ran(repo, "playwright-old-bar", "old/bar.spec.ts",
1128
+ {"daw/AC-16": {"status": "passed"}})
1129
+
1130
+ read = coverage._load_run(repo)
1131
+ assert read.proven["daw/AC-16"] == "failed"
1132
+ assert read.stale == ["old/bar.spec.ts"]
1133
+
1134
+
1069
1135
  # ---------------------------------------------------------------------------
1070
1136
  # Ported from the second fork. Everything below covers a capability that fork
1071
1137
  # had and this base did not: domain hosting, judgement tallying, run-aware
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appchy/jarvis",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "description": "Jarvis — local AI coding assistant CLI",
5
5
  "private": false,
6
6
  "type": "module",
@@ -57,15 +57,15 @@
57
57
  "typescript": "^5.7.0",
58
58
  "vitest": "^2.1.0",
59
59
  "@jarvis/agents": "1.0.0",
60
- "@jarvis/anthropic": "1.0.0",
61
- "@jarvis/board": "0.1.0",
62
60
  "@jarvis/data": "0.1.0",
63
61
  "@jarvis/errors": "1.0.0",
62
+ "@jarvis/board": "0.1.0",
64
63
  "@jarvis/logger": "1.0.0",
65
64
  "@jarvis/rpc": "1.0.0",
66
65
  "@jarvis/types": "1.0.0",
67
66
  "@jarvis/typescript-config": "1.0.0",
68
67
  "@jarvis/ui": "0.1.0",
68
+ "@jarvis/anthropic": "1.0.0",
69
69
  "@jarvis/vitest-config": "1.0.0"
70
70
  },
71
71
  "scripts": {