napkinstack 0.19.0__tar.gz → 0.19.2__tar.gz

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.
Files changed (39) hide show
  1. {napkinstack-0.19.0 → napkinstack-0.19.2}/PKG-INFO +4 -4
  2. {napkinstack-0.19.0 → napkinstack-0.19.2}/README.md +3 -3
  3. {napkinstack-0.19.0 → napkinstack-0.19.2}/pyproject.toml +1 -1
  4. {napkinstack-0.19.0 → napkinstack-0.19.2}/pyproject.toml.orig +1 -1
  5. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/doctor.py +16 -2
  6. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/project.py +1 -1
  7. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/provenance.py +1 -1
  8. {napkinstack-0.19.0 → napkinstack-0.19.2}/LICENSE +0 -0
  9. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/__init__.py +0 -0
  10. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/assurance.py +0 -0
  11. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/attest.py +0 -0
  12. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/board.py +0 -0
  13. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/cli.py +0 -0
  14. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/compat.py +0 -0
  15. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/discovery.py +0 -0
  16. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/evidence.py +0 -0
  17. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/__init__.py +0 -0
  18. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/boundaries.py +0 -0
  19. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/hygiene.py +0 -0
  20. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/manifests.py +0 -0
  21. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/plan.py +0 -0
  22. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/fitness/pr_scope.py +0 -0
  23. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/hold.py +0 -0
  24. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/landed.py +0 -0
  25. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/modules.py +0 -0
  26. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/owners.py +0 -0
  27. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/pull_request.py +0 -0
  28. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/settings.py +0 -0
  29. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/skills.py +0 -0
  30. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/AGENTS.md +0 -0
  31. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/MANIFEST.yaml +0 -0
  32. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/README.md +0 -0
  33. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/docs/runbook.md +0 -0
  34. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/tests/.gitkeep +0 -0
  35. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/module/AGENTS.md +0 -0
  36. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/module/MANIFEST.yaml +0 -0
  37. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/module/README.md +0 -0
  38. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/module/src/.gitkeep +0 -0
  39. {napkinstack-0.19.0 → napkinstack-0.19.2}/src/napkinstack/templates/module/tests/.gitkeep +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napkinstack
3
- Version: 0.19.0
3
+ Version: 0.19.2
4
4
  Summary: Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in CI.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -21,7 +21,7 @@ modules, contracts, guardrails in CI. On the Django or Rails model, one command
21
21
  the project, which then receives new versions on demand; no application stack is imposed.
22
22
  Positioning and vocabulary: [`PRODUCT.md`](PRODUCT.md) §1.
23
23
 
24
- > **Status: v0.19.0, published** on [PyPI](https://pypi.org/project/napkinstack/).
24
+ > **Status: v0.19.2, published** on [PyPI](https://pypi.org/project/napkinstack/).
25
25
  > What each version changes, engine and project apart: [`CHANGELOG.md`](CHANGELOG.md).
26
26
  > What is open: [`docs/governance/workstreams.md`](docs/governance/workstreams.md).
27
27
 
@@ -85,8 +85,8 @@ To try it without installing anything, or to install it from this repository ins
85
85
  registry — always pinned to a release tag:
86
86
 
87
87
  ```bash
88
- uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.19.0" nstack init my-project
89
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.0"
88
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.19.2" nstack init my-project
89
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.2"
90
90
  ```
91
91
 
92
92
  **Two channels, one published artefact.** The registry publishes: the PyPI artefact of a
@@ -5,7 +5,7 @@ modules, contracts, guardrails in CI. On the Django or Rails model, one command
5
5
  the project, which then receives new versions on demand; no application stack is imposed.
6
6
  Positioning and vocabulary: [`PRODUCT.md`](PRODUCT.md) §1.
7
7
 
8
- > **Status: v0.19.0, published** on [PyPI](https://pypi.org/project/napkinstack/).
8
+ > **Status: v0.19.2, published** on [PyPI](https://pypi.org/project/napkinstack/).
9
9
  > What each version changes, engine and project apart: [`CHANGELOG.md`](CHANGELOG.md).
10
10
  > What is open: [`docs/governance/workstreams.md`](docs/governance/workstreams.md).
11
11
 
@@ -69,8 +69,8 @@ To try it without installing anything, or to install it from this repository ins
69
69
  registry — always pinned to a release tag:
70
70
 
71
71
  ```bash
72
- uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.19.0" nstack init my-project
73
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.0"
72
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.19.2" nstack init my-project
73
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.2"
74
74
  ```
75
75
 
76
76
  **Two channels, one published artefact.** The registry publishes: the PyPI artefact of a
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "napkinstack"
3
- version = "0.19.0"
3
+ version = "0.19.2"
4
4
  description = "Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in CI."
5
5
  requires-python = ">=3.12"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "napkinstack"
3
- version = "0.19.0"
3
+ version = "0.19.2"
4
4
  description = "Engineering framework for several teams and their agents working in one repository: modules, contracts, guardrails in CI."
5
5
  requires-python = ">=3.12"
6
6
  license = "MIT"
@@ -405,9 +405,23 @@ def _kept_for_a_human(root: Path, shown: str, text: str) -> tuple[str, str]:
405
405
  if name in UNREAD_OWNERSHIP or name == "none":
406
406
  return OK, ("No forge reads it here: who approves what is for people to read, and nothing refuses "
407
407
  "an agent's approval of the rest.")
408
+ agents = _agent_identities(root)
409
+ if len(agents) < 2:
410
+ return OK, (f"The history shows {len(agents) or 'no'} agent identit{'y' if len(agents) == 1 else 'ies'}"
411
+ + (f" ({agents[0]})" if agents else "") + ": an agent never approves what its own identity "
412
+ "pushed, so until another contributor's agent works under an identity of its own, every "
413
+ "change waits for a human, as on approval human.")
408
414
  return OK, ""
409
415
 
410
416
 
417
+ def _agent_identities(root: Path) -> list[str]:
418
+ """The bot identities the recent history's commits name, as GitHub's noreply addresses say them."""
419
+ shown = subprocess.run(["git", "log", "-n", "500", "--format=%ae%n%ce"], cwd=root, capture_output=True, text=True)
420
+ found = [address.lower() for address in shown.stdout.split()
421
+ if address.lower().endswith("[bot]@users.noreply.github.com")]
422
+ return list(dict.fromkeys(found))
423
+
424
+
411
425
  def _forge(path: str) -> dict | None:
412
426
  """JSON of a public repository's page on the forge, read without a token; None when it does
413
427
  not answer."""
@@ -440,7 +454,7 @@ def _left_behind(answers: dict) -> str | None:
440
454
  def _workstation(root: Path, answers: dict) -> list[tuple[str, str, str, str]]:
441
455
  commit = str(answers.get("_commit") or "")
442
456
  project = commit.removeprefix("v")
443
- install = (f'uv tool install "napkinstack=={project}" --with-executables-from pre-commit'
457
+ install = (f'uv tool install "napkinstack=={project}" --refresh-package napkinstack --with-executables-from pre-commit'
444
458
  if PUBLISHED.fullmatch(commit) else "bash .nstack/install-engine.sh")
445
459
  results = []
446
460
 
@@ -563,7 +577,7 @@ def _newer(answers: dict) -> None:
563
577
  print(f" NEWER VERSION v{latest} is published; the project is on {commit} — information, not a gap\n"
564
578
  f" What it changes: {release_notes(str(answers.get('_src_path') or ''), commit, f'v{latest}')}\n"
565
579
  f" To take it, at a cycle's boundary: uv tool install \"napkinstack=={latest}\" "
566
- "--with-executables-from pre-commit, then nstack update")
580
+ "--refresh-package napkinstack --with-executables-from pre-commit, then nstack update")
567
581
 
568
582
 
569
583
  def _private(client: GitHub) -> bool:
@@ -85,7 +85,7 @@ def _explain(exc: Exception, command: str, where: Path, source: str, ref: str) -
85
85
  # version is not moved by `uv tool upgrade`, which is what a reader tries first.
86
86
  return (f"FAIL [{command}] Target version {match[2]} older than the project version ({match[1]}): "
87
87
  f"no going back.\n{action}move this workstation forward first — "
88
- 'uv tool install "napkinstack@latest" --with-executables-from pre-commit — '
88
+ 'uv tool install "napkinstack@latest" --refresh-package napkinstack --with-executables-from pre-commit — '
89
89
  f"or pin the version you want, {match[1]} or newer.")
90
90
  if text.startswith("Updating is only supported in git-tracked subprojects"):
91
91
  return (f"FAIL [{command}] {where} is not a git repository: the merge relies on "
@@ -135,4 +135,4 @@ def behind(root: Path) -> str | None:
135
135
  return None
136
136
  return (f"This workstation runs nstack {version}; the project is on {project}. A command it names "
137
137
  "may not exist here, and a verdict given here is not CI's.\n"
138
- f' Action: uv tool install "napkinstack=={wanted}" --with-executables-from pre-commit')
138
+ f' Action: uv tool install "napkinstack=={wanted}" --refresh-package napkinstack --with-executables-from pre-commit')
File without changes