napkinstack 0.8.0__tar.gz → 0.9.0__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 (34) hide show
  1. {napkinstack-0.8.0 → napkinstack-0.9.0}/PKG-INFO +4 -4
  2. {napkinstack-0.8.0 → napkinstack-0.9.0}/README.md +3 -3
  3. {napkinstack-0.8.0 → napkinstack-0.9.0}/pyproject.toml +1 -1
  4. {napkinstack-0.8.0 → napkinstack-0.9.0}/pyproject.toml.orig +1 -1
  5. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/pull_request.py +7 -2
  6. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/skills.py +14 -1
  7. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/contracts/AGENTS.md +2 -1
  8. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/module/AGENTS.md +3 -3
  9. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/module/README.md +4 -7
  10. {napkinstack-0.8.0 → napkinstack-0.9.0}/LICENSE +0 -0
  11. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/__init__.py +0 -0
  12. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/assurance.py +0 -0
  13. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/cli.py +0 -0
  14. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/compat.py +0 -0
  15. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/discovery.py +0 -0
  16. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/doctor.py +0 -0
  17. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/__init__.py +0 -0
  18. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/boundaries.py +0 -0
  19. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/hygiene.py +0 -0
  20. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/manifests.py +0 -0
  21. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/plan.py +0 -0
  22. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/fitness/pr_scope.py +0 -0
  23. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/landed.py +0 -0
  24. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/modules.py +0 -0
  25. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/project.py +0 -0
  26. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/provenance.py +0 -0
  27. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/settings.py +0 -0
  28. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/contracts/MANIFEST.yaml +0 -0
  29. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/contracts/README.md +0 -0
  30. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/contracts/docs/runbook.md +0 -0
  31. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/contracts/tests/.gitkeep +0 -0
  32. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/module/MANIFEST.yaml +0 -0
  33. {napkinstack-0.8.0 → napkinstack-0.9.0}/src/napkinstack/templates/module/src/.gitkeep +0 -0
  34. {napkinstack-0.8.0 → napkinstack-0.9.0}/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.8.0
3
+ Version: 0.9.0
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.8.0, published** on [PyPI](https://pypi.org/project/napkinstack/).
23
+ > **Status: v0.9.0, 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.8.0" nstack init my-project
88
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.8.0"
87
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.9.0" nstack init my-project
88
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.9.0"
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.8.0, published** on [PyPI](https://pypi.org/project/napkinstack/).
8
+ > **Status: v0.9.0, 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.8.0" nstack init my-project
73
- uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.8.0"
72
+ uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.9.0" nstack init my-project
73
+ uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.9.0"
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.8.0"
3
+ version = "0.9.0"
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.8.0"
3
+ version = "0.9.0"
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"
@@ -322,7 +322,8 @@ def check_sheet(body: str, head: str, reasons: list[str], fail: Fail,
322
322
  return human_only
323
323
 
324
324
 
325
- def check_cycle(root: Path, body: str, labels: set[str], today: datetime.date, fail: Fail) -> None:
325
+ def check_cycle(root: Path, body: str, labels: set[str], today: datetime.date, fail: Fail,
326
+ warn: Callable[[str], None] = lambda message: None) -> None:
326
327
  """K1 to K4, for delivery work."""
327
328
  if LABEL in labels:
328
329
  if not JUSTIFICATION.search(body):
@@ -364,6 +365,10 @@ def check_cycle(root: Path, body: str, labels: set[str], today: datetime.date, f
364
365
  elif states[match[1]] not in {"ready", "in-progress"}:
365
366
  fail("K3", f"deliverable {match[1]} is '{states[match[1]]}': work starts on a ready "
366
367
  "deliverable (definition of ready).")
368
+ elif states[match[1]] == "ready":
369
+ # Said, not refused: the state is the decider's record, and this pull request knows (PDR-0002).
370
+ warn(f"[K3] deliverable {match[1]} is still 'ready' while this pull request delivers it.\n"
371
+ f" Action: set its state: in-progress in {path.name}, in this pull request.")
367
372
 
368
373
 
369
374
  def charter_without_pdr(root: Path, base: str, files: list[str]) -> list[str]:
@@ -417,7 +422,7 @@ def run(root: Path, base: str, body_file: Path | None = None) -> int:
417
422
  human_only = check_sheet(body, head, reasons, fail, authorship, rerun_at_head(modules),
418
423
  lambda commit: bool(tree_of(commit)) and tree_of(commit) == tree_of(head))
419
424
  if modules:
420
- check_cycle(root, body, labels, datetime.date.today(), fail)
425
+ check_cycle(root, body, labels, datetime.date.today(), fail, warnings.append)
421
426
 
422
427
  print(f"Modules touched : {len(modules)}" + "".join(f"\n - {folder}" for folder in modules))
423
428
  print(f"Test sheet : {'required' if reasons else 'not required'}")
@@ -48,11 +48,24 @@ def normalise(text: str) -> str:
48
48
  return " ".join(text.split())
49
49
 
50
50
 
51
+ def trigger(playbook: str) -> str:
52
+ """A playbook's trigger, as its header says it — the kernel's words (§4), on one line."""
53
+ match = re.search(r"^> \*\*Trigger\.\*\* (.+?)(?=\n>\s*\n|\n\n|\Z)", playbook, re.S | re.M)
54
+ return normalise(match.group(1).replace("\n> ", " ")) if match else ""
55
+
56
+
57
+ def described(entry: dict, playbook: str) -> str:
58
+ """What the skill holds, from the mapping, and when it loads, from its playbook: one list of
59
+ triggers, the kernel's, which the playbook's header says again word for word."""
60
+ when = trigger(playbook)
61
+ return normalise(entry["description"]) + (f" {when}" if when else "")
62
+
63
+
51
64
  def build(root: Path, name: str, entry: dict) -> tuple[Path, str]:
52
65
  body = (root / entry["source"]).read_text(encoding="utf-8")
53
66
  # Serialised, never concatenated: a ":" or "#" in a description would break the YAML.
54
67
  frontmatter = yaml.safe_dump(
55
- {"name": name, "description": normalise(entry["description"])},
68
+ {"name": name, "description": described(entry, body)},
56
69
  allow_unicode=True, sort_keys=False, width=float("inf"),
57
70
  )
58
71
  content = "---\n" + frontmatter + "---\n\n" + BANNER.format(source=entry["source"]) + "\n\n" + body
@@ -22,5 +22,6 @@ contract tests do.
22
22
 
23
23
  ## Before changing anything
24
24
 
25
- Check the consumer matrix, generated from the manifests. A version is only removed once
25
+ Check who consumes it, read from the manifests: `nstack modules --with-contract-sides`, and
26
+ B6 for a version consumed and provided by nobody. A version is only removed once
26
27
  `consumers = 0`.
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## Responsibility
10
10
 
11
- <One sentence. The same as the one in the MANIFEST.>
11
+ The one sentence of `MANIFEST.yaml` (`module.responsibility`); not repeated here.
12
12
 
13
13
  ## What this module does not do
14
14
 
@@ -24,8 +24,8 @@
24
24
 
25
25
  ## Known traps
26
26
 
27
- <What has already broken here. Every entry should become a test - see
28
- .nstack/handbook/10-measurement.md §4.>
27
+ <One line per trap — the situation, then what to do. Every entry becomes a test
28
+ (.nstack/handbook/10-measurement.md §4); a story of what happened goes to the log issue.>
29
29
 
30
30
  ## Areas not to change without approval
31
31
 
@@ -1,17 +1,14 @@
1
1
  # {{MODULE_NAME}}
2
2
 
3
- <One sentence: what this module is for.>
4
-
5
- - **Owner**: {{OWNER}}
6
- - **Criticality**: {{CRITICALITY}}
7
- - **Manifest**: [MANIFEST.yaml](./MANIFEST.yaml)
3
+ What this module is for, who owns it and how critical it is: its
4
+ [MANIFEST.yaml](./MANIFEST.yaml), the one place they are written.
8
5
 
9
6
  <A diagram, with its legend: what this module provides, what it consumes, who uses it.>
10
7
 
11
8
  ## Getting started
12
9
 
13
- The standard verbs — `bootstrap`, `check`, `test`, `run` — are declared in the `commands`
14
- section of the [manifest](./MANIFEST.yaml), for this module's stack. What each one must do:
10
+ The standard verbs are declared in the `commands` section of the [manifest](./MANIFEST.yaml),
11
+ for this module's stack. Which they are, and what each one must do:
15
12
  `.nstack/handbook/09-platform.md` §2.
16
13
 
17
14
  ## Contracts
File without changes