napkinstack 0.19.1__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.1 → napkinstack-0.19.2}/PKG-INFO +4 -4
  2. {napkinstack-0.19.1 → napkinstack-0.19.2}/README.md +3 -3
  3. {napkinstack-0.19.1 → napkinstack-0.19.2}/pyproject.toml +1 -1
  4. {napkinstack-0.19.1 → napkinstack-0.19.2}/pyproject.toml.orig +1 -1
  5. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/doctor.py +14 -0
  6. {napkinstack-0.19.1 → napkinstack-0.19.2}/LICENSE +0 -0
  7. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/__init__.py +0 -0
  8. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/assurance.py +0 -0
  9. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/attest.py +0 -0
  10. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/board.py +0 -0
  11. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/cli.py +0 -0
  12. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/compat.py +0 -0
  13. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/discovery.py +0 -0
  14. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/evidence.py +0 -0
  15. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/__init__.py +0 -0
  16. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/boundaries.py +0 -0
  17. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/hygiene.py +0 -0
  18. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/manifests.py +0 -0
  19. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/plan.py +0 -0
  20. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/fitness/pr_scope.py +0 -0
  21. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/hold.py +0 -0
  22. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/landed.py +0 -0
  23. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/modules.py +0 -0
  24. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/owners.py +0 -0
  25. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/project.py +0 -0
  26. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/provenance.py +0 -0
  27. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/pull_request.py +0 -0
  28. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/settings.py +0 -0
  29. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/skills.py +0 -0
  30. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/AGENTS.md +0 -0
  31. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/MANIFEST.yaml +0 -0
  32. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/README.md +0 -0
  33. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/docs/runbook.md +0 -0
  34. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/contracts/tests/.gitkeep +0 -0
  35. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/module/AGENTS.md +0 -0
  36. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/module/MANIFEST.yaml +0 -0
  37. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/module/README.md +0 -0
  38. {napkinstack-0.19.1 → napkinstack-0.19.2}/src/napkinstack/templates/module/src/.gitkeep +0 -0
  39. {napkinstack-0.19.1 → 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.1
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.1, 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.1" nstack init my-project
89
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.1"
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.1, 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.1" nstack init my-project
73
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.19.1"
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.1"
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.1"
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."""
File without changes