napkinstack 0.8.0__tar.gz → 0.9.1__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.
- {napkinstack-0.8.0 → napkinstack-0.9.1}/PKG-INFO +4 -4
- {napkinstack-0.8.0 → napkinstack-0.9.1}/README.md +3 -3
- {napkinstack-0.8.0 → napkinstack-0.9.1}/pyproject.toml +1 -1
- {napkinstack-0.8.0 → napkinstack-0.9.1}/pyproject.toml.orig +1 -1
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/pull_request.py +7 -2
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/skills.py +14 -1
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/contracts/AGENTS.md +2 -1
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/module/AGENTS.md +3 -3
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/module/README.md +4 -7
- {napkinstack-0.8.0 → napkinstack-0.9.1}/LICENSE +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/__init__.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/assurance.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/cli.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/compat.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/discovery.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/doctor.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/__init__.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/boundaries.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/hygiene.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/manifests.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/plan.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/fitness/pr_scope.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/landed.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/modules.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/project.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/provenance.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/settings.py +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/contracts/MANIFEST.yaml +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/contracts/README.md +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/contracts/docs/runbook.md +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/contracts/tests/.gitkeep +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/module/MANIFEST.yaml +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/src/napkinstack/templates/module/src/.gitkeep +0 -0
- {napkinstack-0.8.0 → napkinstack-0.9.1}/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.
|
|
3
|
+
Version: 0.9.1
|
|
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.
|
|
23
|
+
> **Status: v0.9.1, 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.
|
|
88
|
-
uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.
|
|
87
|
+
uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.9.1" nstack init my-project
|
|
88
|
+
uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.9.1"
|
|
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
|
+
> **Status: v0.9.1, 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.
|
|
73
|
-
uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.
|
|
72
|
+
uvx --from "git+https://github.com/NapkinStack/engineering-os@v0.9.1" nstack init my-project
|
|
73
|
+
uv tool install "napkinstack @ git+https://github.com/NapkinStack/engineering-os@v0.9.1"
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
**Two channels, one published artefact.** The registry publishes: the PyPI artefact of a
|
|
@@ -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
|
|
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":
|
|
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
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
|
14
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|