@appchy/jarvis 0.1.39 → 0.1.40
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 +8 -3
- package/dist/bin.js.map +1 -1
- package/harness/harness/gate.py +7 -1
- package/harness/harness/git.py +51 -0
- package/harness/test_work.py +47 -0
- package/package.json +1 -1
package/harness/harness/gate.py
CHANGED
|
@@ -485,8 +485,14 @@ def gate(root, task, accept: str = "", owner: str = "") -> list:
|
|
|
485
485
|
f"`jarvis work verify --task {task.name}`")
|
|
486
486
|
else:
|
|
487
487
|
head = _head(root.parent)
|
|
488
|
+
# WHAT MOVED, not merely THAT it moved. A board write cannot change what a
|
|
489
|
+
# test proved, and neither can a commit inside a system this task does not
|
|
490
|
+
# own (`M-86`, and `git.moved_only_elsewhere` for the measurement and for
|
|
491
|
+
# why a path nothing claims stays strict).
|
|
488
492
|
if (head and sha not in ("no-git", "") and not head.startswith(sha)
|
|
489
|
-
and not git.only_board_moved(root.parent, sha)
|
|
493
|
+
and not git.only_board_moved(root.parent, sha)
|
|
494
|
+
and not git.moved_only_elsewhere(
|
|
495
|
+
root.parent, sha, root, list(task.code or []))):
|
|
490
496
|
reasons.append(f"code moved since verify passed (proven at {sha}, "
|
|
491
497
|
f"HEAD is {head[:12]}) — re-run `jarvis work verify "
|
|
492
498
|
f"--task {task.name}`")
|
package/harness/harness/git.py
CHANGED
|
@@ -423,6 +423,57 @@ def only_board_moved(repo, sha: str) -> bool:
|
|
|
423
423
|
for f in out.splitlines() if f.strip())
|
|
424
424
|
|
|
425
425
|
|
|
426
|
+
def moved_only_elsewhere(repo, sha: str, root, regions: list) -> bool:
|
|
427
|
+
"""Is everything between `sha` and HEAD outside the systems that own `regions`?
|
|
428
|
+
|
|
429
|
+
**The completion gate is a race, and this is what makes it winnable.** It refuses
|
|
430
|
+
unless the verify sha IS HEAD, which is right in spirit — evidence is about the
|
|
431
|
+
tree it was taken on. On a tree several sessions share it is close to unwinnable:
|
|
432
|
+
MEASURED on mixbrix 2026-09-07, the median gap between commits that touch code is
|
|
433
|
+
284s and a gate run is 287s, so about half of all attempts finish their run only to
|
|
434
|
+
be told the code moved. A session that stops there leaves finished work sitting in
|
|
435
|
+
`in-progress` for good — `the-benches-become-sessions` sat there a fortnight with
|
|
436
|
+
every criterion ticked and its own handoff saying exactly why.
|
|
437
|
+
|
|
438
|
+
**This is not a new licence; it is `M-86` applied one step earlier.** That rule
|
|
439
|
+
already says a suite failure OUTSIDE your `code:` regions warns rather than
|
|
440
|
+
refuses. If a failure over there cannot refuse you, a COMMIT over there cannot
|
|
441
|
+
invalidate you either — holding both at once was the inconsistency.
|
|
442
|
+
|
|
443
|
+
**A path no system claims counts as the code moving, and that is the whole safety
|
|
444
|
+
of it.** `system_for_path` answers `None` both for a file nothing declares and for
|
|
445
|
+
a repo whose systems declare nothing, and the two are indistinguishable from here.
|
|
446
|
+
Reading either as *outside your regions* would turn every repo that never filled in
|
|
447
|
+
`paths:` into one where the gate silently passes anything — a vacuous green, which
|
|
448
|
+
is worse than the race it replaces. So unknown is always strict, and a repo earns
|
|
449
|
+
this only by declaring where its code lives.
|
|
450
|
+
"""
|
|
451
|
+
if not enabled() or not sha or not regions:
|
|
452
|
+
return False
|
|
453
|
+
code, out, _ = _git(repo, "diff", "--name-only", f"{sha}..HEAD")
|
|
454
|
+
if code != 0:
|
|
455
|
+
return False
|
|
456
|
+
from .registry import scan_systems, system_for_path
|
|
457
|
+
systems = scan_systems(root)
|
|
458
|
+
if not systems:
|
|
459
|
+
return False
|
|
460
|
+
mine = {r for r in regions if r}
|
|
461
|
+
board = [r.rstrip("/") for r in (GIT.get("paths") or [])]
|
|
462
|
+
moved = False
|
|
463
|
+
for name in out.splitlines():
|
|
464
|
+
name = name.strip()
|
|
465
|
+
if not name:
|
|
466
|
+
continue
|
|
467
|
+
if any(name == r or name.startswith(f"{r}/") for r in board):
|
|
468
|
+
continue
|
|
469
|
+
owner = system_for_path(root, name, systems)
|
|
470
|
+
# Nothing claims it, or one of MY systems does -> the evidence is stale.
|
|
471
|
+
if owner is None or (mine & set(owner.code)):
|
|
472
|
+
return False
|
|
473
|
+
moved = True
|
|
474
|
+
return moved
|
|
475
|
+
|
|
476
|
+
|
|
426
477
|
def land(repo, item: str, rows: list) -> tuple:
|
|
427
478
|
"""Commit what changed and push it. Returns (committed, pushed, note).
|
|
428
479
|
|
package/harness/test_work.py
CHANGED
|
@@ -3140,6 +3140,53 @@ def test_a_board_commit_does_not_count_as_the_code_moving():
|
|
|
3140
3140
|
config.apply(config.DEFAULTS)
|
|
3141
3141
|
|
|
3142
3142
|
|
|
3143
|
+
def test_a_commit_in_another_system_does_not_invalidate_your_verify():
|
|
3144
|
+
# The completion gate refused unless the verify sha WAS head, which on a tree
|
|
3145
|
+
# several sessions share is close to unwinnable — measured on mixbrix, the median
|
|
3146
|
+
# gap between code commits is about as long as a gate run, so half of all attempts
|
|
3147
|
+
# lose. `M-86` already says a suite failure outside your `code:` regions warns
|
|
3148
|
+
# rather than refuses; a COMMIT outside them cannot invalidate you either.
|
|
3149
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
3150
|
+
try:
|
|
3151
|
+
repo = _git_repo(tmp)
|
|
3152
|
+
arch = repo / "work" / "architecture"
|
|
3153
|
+
arch.mkdir(parents=True, exist_ok=True)
|
|
3154
|
+
(arch / "backoffice.md").write_text(
|
|
3155
|
+
"---\ntype: system\ncode: [backoffice]\npaths: [apps/backoffice]\n"
|
|
3156
|
+
"depends_on: []\n---\n\n# Backoffice\n")
|
|
3157
|
+
(arch / "engine.md").write_text(
|
|
3158
|
+
"---\ntype: system\ncode: [runtime]\npaths: [packages/engine]\n"
|
|
3159
|
+
"depends_on: []\n---\n\n# Engine\n")
|
|
3160
|
+
for f in ("apps/backoffice/page.tsx", "packages/engine/run.cpp",
|
|
3161
|
+
"scripts/loose.py"):
|
|
3162
|
+
(repo / f).parent.mkdir(parents=True, exist_ok=True)
|
|
3163
|
+
(repo / f).write_text("first\n")
|
|
3164
|
+
_git(repo, "add", "-A")
|
|
3165
|
+
_git(repo, "commit", "-qm", "the code under test")
|
|
3166
|
+
proven_at = _git(repo, "rev-parse", "HEAD").stdout.strip()[:12]
|
|
3167
|
+
root = repo / "work"
|
|
3168
|
+
|
|
3169
|
+
# A PEER MOVES ANOTHER SYSTEM. Your evidence still stands.
|
|
3170
|
+
(repo / "packages/engine/run.cpp").write_text("a peer's change\n")
|
|
3171
|
+
_git(repo, "add", "-A")
|
|
3172
|
+
_git(repo, "commit", "-qm", "somebody else's system")
|
|
3173
|
+
assert git.moved_only_elsewhere(repo, proven_at, root, ["backoffice"]), \
|
|
3174
|
+
"a commit in a system you do not own cannot invalidate your run"
|
|
3175
|
+
assert not git.moved_only_elsewhere(repo, proven_at, root, ["runtime"]), \
|
|
3176
|
+
"and the very same commit DOES invalidate the task that owns it"
|
|
3177
|
+
|
|
3178
|
+
# A PATH NOTHING CLAIMS IS ALWAYS STRICT. This is the safety property: a
|
|
3179
|
+
# repo that never filled in `paths:` must not get a gate that passes
|
|
3180
|
+
# everything, which is a worse failure than the race it replaces.
|
|
3181
|
+
(repo / "scripts/loose.py").write_text("claimed by no system\n")
|
|
3182
|
+
_git(repo, "add", "-A")
|
|
3183
|
+
_git(repo, "commit", "-qm", "a file under no system at all")
|
|
3184
|
+
assert not git.moved_only_elsewhere(repo, proven_at, root, ["backoffice"]), \
|
|
3185
|
+
"a path no system declares must count as the code moving"
|
|
3186
|
+
finally:
|
|
3187
|
+
config.apply(config.DEFAULTS)
|
|
3188
|
+
|
|
3189
|
+
|
|
3143
3190
|
def test_a_repo_that_asked_for_git_and_HAS_none_is_told_so():
|
|
3144
3191
|
# The silent case, and the worst one: `git.commit` is set, there is no repository
|
|
3145
3192
|
# under the tree, and the write reaches the disk with nothing said. That is the
|