napkinstack 0.7.2__tar.gz → 0.7.3__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 (35) hide show
  1. {napkinstack-0.7.2 → napkinstack-0.7.3}/PKG-INFO +4 -4
  2. {napkinstack-0.7.2 → napkinstack-0.7.3}/README.md +3 -3
  3. {napkinstack-0.7.2 → napkinstack-0.7.3}/pyproject.toml +1 -1
  4. {napkinstack-0.7.2 → napkinstack-0.7.3}/pyproject.toml.orig +1 -1
  5. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/cli.py +15 -0
  6. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/provenance.py +23 -0
  7. {napkinstack-0.7.2 → napkinstack-0.7.3}/LICENSE +0 -0
  8. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/__init__.py +0 -0
  9. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/assurance.py +0 -0
  10. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/compat.py +0 -0
  11. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/discovery.py +0 -0
  12. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/doctor.py +0 -0
  13. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/__init__.py +0 -0
  14. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/boundaries.py +0 -0
  15. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/hygiene.py +0 -0
  16. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/manifests.py +0 -0
  17. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/plan.py +0 -0
  18. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/fitness/pr_scope.py +0 -0
  19. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/landed.py +0 -0
  20. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/modules.py +0 -0
  21. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/project.py +0 -0
  22. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/pull_request.py +0 -0
  23. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/settings.py +0 -0
  24. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/skills.py +0 -0
  25. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/contracts/AGENTS.md +0 -0
  26. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/contracts/MANIFEST.yaml +0 -0
  27. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/contracts/README.md +0 -0
  28. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/contracts/docs/runbook.md +0 -0
  29. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/contracts/tests/.gitkeep +0 -0
  30. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/module/AGENTS.md +0 -0
  31. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/module/MANIFEST.yaml +0 -0
  32. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/module/README.md +0 -0
  33. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/module/docs/adr/.gitkeep +0 -0
  34. {napkinstack-0.7.2 → napkinstack-0.7.3}/src/napkinstack/templates/module/src/.gitkeep +0 -0
  35. {napkinstack-0.7.2 → napkinstack-0.7.3}/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.7.2
3
+ Version: 0.7.3
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
@@ -20,7 +20,7 @@ modules, contracts, guardrails in CI. On the Django or Rails model, one command
20
20
  the project, which then receives new versions on demand; no application stack is imposed.
21
21
  Positioning and vocabulary: [`PRODUCT.md`](PRODUCT.md) §1.
22
22
 
23
- > **Status: v0.7.2, published** on [PyPI](https://pypi.org/project/napkinstack/).
23
+ > **Status: v0.7.3, published** on [PyPI](https://pypi.org/project/napkinstack/).
24
24
  > What each version changes, engine and project apart: [`CHANGELOG.md`](CHANGELOG.md).
25
25
  > What is open: [`docs/governance/workstreams.md`](docs/governance/workstreams.md).
26
26
 
@@ -84,8 +84,8 @@ To try it without installing anything, or to install it from this repository ins
84
84
  registry — always pinned to a release tag:
85
85
 
86
86
  ```bash
87
- uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.7.2" nstack init my-project
88
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.7.2"
87
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.7.3" nstack init my-project
88
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.7.3"
89
89
  ```
90
90
 
91
91
  **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.7.2, published** on [PyPI](https://pypi.org/project/napkinstack/).
8
+ > **Status: v0.7.3, 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.7.2" nstack init my-project
73
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.7.2"
72
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.7.3" nstack init my-project
73
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.7.3"
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.7.2"
3
+ version = "0.7.3"
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.7.2"
3
+ version = "0.7.3"
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"
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import json
7
+ import sys
7
8
  from pathlib import Path
8
9
 
9
10
  from napkinstack import assurance
@@ -148,7 +149,21 @@ JUDGES = {"manifests", "boundaries", "plan", "skills", "hygiene", "fitness", "do
148
149
  "pr-check", "compat", "landed"} # the commands whose verdict depends on the framework's rules
149
150
 
150
151
 
152
+ def _named_root(argv: list[str]) -> Path:
153
+ """The root a command line names, read before the line is parsed: a command this engine
154
+ does not know is refused by the parser, and the reason may be the engine's age (D90)."""
155
+ for index, word in enumerate(argv):
156
+ if word == "--root" and index + 1 < len(argv):
157
+ return Path(argv[index + 1])
158
+ if word.startswith("--root="):
159
+ return Path(word.removeprefix("--root="))
160
+ return Path(".")
161
+
162
+
151
163
  def main(argv: list[str] | None = None) -> int:
164
+ argv = sys.argv[1:] if argv is None else argv
165
+ if late := provenance.behind(_named_root(argv)):
166
+ print(late, file=sys.stderr, flush=True)
152
167
  args = build_parser().parse_args(argv)
153
168
  if args.command in JUDGES:
154
169
  print(provenance.judged_by(args.root), flush=True)
@@ -68,3 +68,26 @@ def judged_by(root: Path) -> str:
68
68
  return (f"Judged by an UNPUBLISHED NapkinStack — {source}"
69
69
  + (f", project pinned to {unpublished}" if unpublished else on)
70
70
  + ": these rules are no published version.")
71
+
72
+
73
+ def behind(root: Path) -> str | None:
74
+ """What a workstation is told when its engine is older than the project it runs in (D90).
75
+
76
+ A teammate's update, once merged, leaves every other workstation there: a command the
77
+ project's manual names answers "invalid choice", and a verdict comes from rules the project
78
+ has left. Said before anything else, on every command — the one that does not exist included.
79
+ An engine ahead of the project is not said: it is the state an update starts from."""
80
+ project = recorded(root)
81
+ if not project or not PUBLISHED.fullmatch(project):
82
+ return None
83
+ version, _ = engine()
84
+ try:
85
+ here = tuple(int(part) for part in version.split(".")[:3])
86
+ except ValueError:
87
+ return None
88
+ wanted = project.removeprefix("v")
89
+ if here >= tuple(int(part) for part in wanted.split(".")):
90
+ return None
91
+ return (f"This workstation runs nstack {version}; the project is on {project}. A command it names "
92
+ "may not exist here, and a verdict given here is not CI's.\n"
93
+ f' Action: uv tool install "napkinstack=={wanted}" --with-executables-from pre-commit')
File without changes