engineering-process 1.2.2__tar.gz → 1.2.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 (76) hide show
  1. {engineering_process-1.2.2/engineering_process.egg-info → engineering_process-1.2.3}/PKG-INFO +1 -1
  2. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/__init__.py +1 -1
  3. {engineering_process-1.2.2 → engineering_process-1.2.3/engineering_process.egg-info}/PKG-INFO +1 -1
  4. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process.egg-info/SOURCES.txt +1 -0
  5. {engineering_process-1.2.2 → engineering_process-1.2.3}/pyproject.toml +1 -1
  6. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_automation.py +27 -0
  7. engineering_process-1.2.3/tests/test_pypi_cache_horizon.py +81 -0
  8. {engineering_process-1.2.2 → engineering_process-1.2.3}/LICENSE +0 -0
  9. {engineering_process-1.2.2 → engineering_process-1.2.3}/MANIFEST.in +0 -0
  10. {engineering_process-1.2.2 → engineering_process-1.2.3}/README.md +0 -0
  11. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/__main__.py +0 -0
  12. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/_supervisor_contract.py +0 -0
  13. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/_supervisor_posix.py +0 -0
  14. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/_supervisor_windows.py +0 -0
  15. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/_windows_job.py +0 -0
  16. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/adoption.py +0 -0
  17. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/cli.py +0 -0
  18. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/commands.py +0 -0
  19. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/contracts.py +0 -0
  20. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/distribution.py +0 -0
  21. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/helper_launch.py +0 -0
  22. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/lifecycle.py +0 -0
  23. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/production_engineering.py +0 -0
  24. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/project.py +0 -0
  25. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/publication_compat.py +0 -0
  26. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/release.py +0 -0
  27. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/repository.py +0 -0
  28. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/requirements-dev.txt +0 -0
  29. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/requirements-runtime.txt +0 -0
  30. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/skills.py +0 -0
  31. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process/supervision.py +0 -0
  32. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process.egg-info/dependency_links.txt +0 -0
  33. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process.egg-info/entry_points.txt +0 -0
  34. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process.egg-info/requires.txt +0 -0
  35. {engineering_process-1.2.2 → engineering_process-1.2.3}/engineering_process.egg-info/top_level.txt +0 -0
  36. {engineering_process-1.2.2 → engineering_process-1.2.3}/process-graph.json +0 -0
  37. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/finish-change/SKILL.md +0 -0
  38. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/implement-change/SKILL.md +0 -0
  39. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/improve-process/SKILL.md +0 -0
  40. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/plan-change/SKILL.md +0 -0
  41. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/production-engineering/SKILL.md +0 -0
  42. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/production-engineering/invariants.json +0 -0
  43. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/review-change/SKILL.md +0 -0
  44. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/run-change/SKILL.md +0 -0
  45. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/start-change/SKILL.md +0 -0
  46. {engineering_process-1.2.2 → engineering_process-1.2.3}/process_assets/skills/verify-change/SKILL.md +0 -0
  47. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/change.schema.json +0 -0
  48. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/plan.schema.json +0 -0
  49. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/process-graph.schema.json +0 -0
  50. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/process-lock.schema.json +0 -0
  51. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/production-engineering.schema.json +0 -0
  52. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/project-legacy.schema.json +0 -0
  53. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/project.schema.json +0 -0
  54. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/receipt.schema.json +0 -0
  55. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/release-change.schema.json +0 -0
  56. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/release.schema.json +0 -0
  57. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/review.schema.json +0 -0
  58. {engineering_process-1.2.2 → engineering_process-1.2.3}/schemas/run.schema.json +0 -0
  59. {engineering_process-1.2.2 → engineering_process-1.2.3}/setup.cfg +0 -0
  60. {engineering_process-1.2.2 → engineering_process-1.2.3}/templates/AGENTS.process.md +0 -0
  61. {engineering_process-1.2.2 → engineering_process-1.2.3}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
  62. {engineering_process-1.2.2 → engineering_process-1.2.3}/templates/adopt-process-windows-job.py +0 -0
  63. {engineering_process-1.2.2 → engineering_process-1.2.3}/templates/adopt-process.py +0 -0
  64. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_adoption.py +0 -0
  65. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_architecture.py +0 -0
  66. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_cli.py +0 -0
  67. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_commands.py +0 -0
  68. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_contracts.py +0 -0
  69. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_distribution.py +0 -0
  70. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_lifecycle.py +0 -0
  71. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_production_engineering.py +0 -0
  72. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_publication_compat.py +0 -0
  73. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_release.py +0 -0
  74. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_repository.py +0 -0
  75. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_skills.py +0 -0
  76. {engineering_process-1.2.2 → engineering_process-1.2.3}/tests/test_supervisor_posix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-process
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A small agent-neutral engineering lifecycle with managed adoption
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/phuongnse/engineering-process
@@ -1,3 +1,3 @@
1
1
  """Agent-neutral engineering process."""
2
2
 
3
- VERSION = "1.2.2"
3
+ VERSION = "1.2.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-process
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A small agent-neutral engineering lifecycle with managed adoption
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/phuongnse/engineering-process
@@ -67,6 +67,7 @@ tests/test_distribution.py
67
67
  tests/test_lifecycle.py
68
68
  tests/test_production_engineering.py
69
69
  tests/test_publication_compat.py
70
+ tests/test_pypi_cache_horizon.py
70
71
  tests/test_release.py
71
72
  tests/test_repository.py
72
73
  tests/test_skills.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "engineering-process"
7
- version = "1.2.2"
7
+ version = "1.2.3"
8
8
  description = "A small agent-neutral engineering lifecycle with managed adoption"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -105,6 +105,31 @@ class AutomationTests(unittest.TestCase):
105
105
  " dispatch-adoption:\n name: Dispatch consumer adoption\n", workflow
106
106
  )
107
107
  self.assertIn("needs: [metadata, publish]", workflow)
108
+ dispatch_job = workflow.split("\n dispatch-adoption:\n", maxsplit=1)[1]
109
+ self.assertIn(" timeout-minutes: 20\n", dispatch_job)
110
+ cache_wait = dispatch_job.split(
111
+ " - name: Wait for consumer Simple caches to expire\n", maxsplit=1
112
+ )[1].split(
113
+ " - name: Create short-lived Renovate event token\n", maxsplit=1
114
+ )[0]
115
+ self.assertIn(
116
+ "python verification/wait_for_pypi_cache_horizon.py",
117
+ cache_wait,
118
+ )
119
+ self.assertIn("${{ steps.release.outputs.published_at }}", cache_wait)
120
+ self.assertIn(
121
+ "ref: ${{ github.sha }}",
122
+ dispatch_job,
123
+ )
124
+ self.assertNotIn("ref: ${{ needs.metadata.outputs.source_sha }}", dispatch_job)
125
+ self.assertIn(
126
+ 'published_at=$(gh release view "$RELEASE_TAG"',
127
+ dispatch_job,
128
+ )
129
+ self.assertLess(
130
+ dispatch_job.index(" - name: Bind the published distribution\n"),
131
+ dispatch_job.index(" - name: Wait for consumer Simple caches to expire\n"),
132
+ )
108
133
  self.assertIn("Verify PyPI exposes the exact built hashes", workflow)
109
134
  visibility = workflow.split(
110
135
  " - name: Verify PyPI exposes the exact built hashes\n", maxsplit=1
@@ -142,6 +167,8 @@ class AutomationTests(unittest.TestCase):
142
167
  publish_job = workflow.split(" publish:\n", maxsplit=1)[1].split(
143
168
  "\n dispatch-adoption:\n", maxsplit=1
144
169
  )[0]
170
+ self.assertIn("ref: ${{ needs.metadata.outputs.source_sha }}", publish_job)
171
+ self.assertNotIn("ref: ${{ github.sha }}", publish_job)
145
172
  self.assertIn("id: release-token", publish_job)
146
173
  self.assertIn("repositories: engineering-process", publish_job)
147
174
  self.assertIn("permission-contents: write", publish_job)
@@ -0,0 +1,81 @@
1
+ from datetime import datetime, timezone
2
+ from email.message import Message
3
+ import unittest
4
+ from unittest.mock import patch
5
+
6
+ from verification.wait_for_pypi_cache_horizon import (
7
+ MAX_SIMPLE_CACHE_SECONDS,
8
+ read_simple_max_age,
9
+ parse_max_age,
10
+ remaining_seconds,
11
+ )
12
+
13
+
14
+ class _Response:
15
+ def __init__(self, headers: Message) -> None:
16
+ self.headers = headers
17
+
18
+ def __enter__(self) -> "_Response":
19
+ return self
20
+
21
+ def __exit__(self, *args: object) -> None:
22
+ return None
23
+
24
+
25
+ class PyPICacheHorizonTests(unittest.TestCase):
26
+ NOW = datetime(2026, 9, 4, 13, 30, tzinfo=timezone.utc)
27
+
28
+ def test_parses_current_bounded_header(self) -> None:
29
+ self.assertEqual(600, parse_max_age(["max-age=600, public"]))
30
+ self.assertEqual(
31
+ MAX_SIMPLE_CACHE_SECONDS,
32
+ parse_max_age([f"public, max-age={MAX_SIMPLE_CACHE_SECONDS}"]),
33
+ )
34
+
35
+ def test_rejects_missing_duplicate_malformed_and_excessive_max_age(self) -> None:
36
+ invalid = [
37
+ None,
38
+ ["public"],
39
+ ["max-age=600", "max-age=300"],
40
+ ["max-age=-1"],
41
+ ["max-age=invalid"],
42
+ [f"max-age={MAX_SIMPLE_CACHE_SECONDS + 1}"],
43
+ ]
44
+ for values in invalid:
45
+ with self.subTest(values=values), self.assertRaises(RuntimeError):
46
+ parse_max_age(values)
47
+
48
+ def test_reads_every_cache_control_field(self) -> None:
49
+ headers = Message()
50
+ headers["Content-Type"] = "application/vnd.pypi.simple.v1+json"
51
+ headers["Cache-Control"] = "public, max-age=600"
52
+ headers["Cache-Control"] = "max-age=300"
53
+ with patch(
54
+ "verification.wait_for_pypi_cache_horizon.urlopen",
55
+ return_value=_Response(headers),
56
+ ), self.assertRaises(RuntimeError):
57
+ read_simple_max_age()
58
+
59
+ def test_current_horizon_and_elapsed_resume(self) -> None:
60
+ self.assertEqual(
61
+ 305,
62
+ remaining_seconds("2026-09-04T13:20:00Z", 900, now=self.NOW),
63
+ )
64
+ self.assertEqual(
65
+ 0,
66
+ remaining_seconds("2026-09-04T13:00:00Z", 600, now=self.NOW),
67
+ )
68
+
69
+ def test_rejects_malformed_naive_and_future_timestamps(self) -> None:
70
+ invalid = [
71
+ "invalid",
72
+ "2026-09-04T13:20:00",
73
+ "2026-09-04T13:31:01Z",
74
+ ]
75
+ for published_at in invalid:
76
+ with self.subTest(published_at=published_at), self.assertRaises(RuntimeError):
77
+ remaining_seconds(published_at, 600, now=self.NOW)
78
+
79
+
80
+ if __name__ == "__main__":
81
+ unittest.main()