engineering-process 1.2.3__tar.gz → 1.2.5__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.
- {engineering_process-1.2.3/engineering_process.egg-info → engineering_process-1.2.5}/PKG-INFO +13 -1
- {engineering_process-1.2.3 → engineering_process-1.2.5}/README.md +12 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/__init__.py +1 -1
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/production_engineering.py +6 -1
- {engineering_process-1.2.3 → engineering_process-1.2.5/engineering_process.egg-info}/PKG-INFO +13 -1
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/production-engineering/SKILL.md +5 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/review-change/SKILL.md +25 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/pyproject.toml +1 -1
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_lifecycle.py +65 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_production_engineering.py +25 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_release.py +43 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/LICENSE +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/MANIFEST.in +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/__main__.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_contract.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_posix.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_windows.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_windows_job.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/adoption.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/cli.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/commands.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/contracts.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/distribution.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/helper_launch.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/lifecycle.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/project.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/publication_compat.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/release.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/repository.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/requirements-dev.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/requirements-runtime.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/skills.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/supervision.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/SOURCES.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/dependency_links.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/entry_points.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/requires.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/top_level.txt +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process-graph.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/finish-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/implement-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/improve-process/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/plan-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/production-engineering/invariants.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/run-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/start-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/verify-change/SKILL.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/change.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/plan.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/process-graph.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/process-lock.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/production-engineering.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/project-legacy.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/project.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/receipt.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/release-change.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/release.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/review.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/run.schema.json +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/setup.cfg +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/templates/AGENTS.process.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/templates/adopt-process-windows-job.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/templates/adopt-process.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_adoption.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_architecture.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_automation.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_cli.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_commands.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_contracts.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_distribution.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_publication_compat.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_pypi_cache_horizon.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_repository.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_skills.py +0 -0
- {engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_supervisor_posix.py +0 -0
{engineering_process-1.2.3/engineering_process.egg-info → engineering_process-1.2.5}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
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
|
|
@@ -352,6 +352,10 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
352
352
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
353
353
|
the authority that started the relevant phase.
|
|
354
354
|
|
|
355
|
+
The [finding priority definitions](process_assets/skills/review-change/SKILL.md#finding-priority)
|
|
356
|
+
are the canonical P0-P3 impact convention for this process, including examples and
|
|
357
|
+
their relationship to blocking decisions.
|
|
358
|
+
|
|
355
359
|
## Running a change
|
|
356
360
|
|
|
357
361
|
Create and validate a change contract, then register it:
|
|
@@ -437,6 +441,14 @@ At any point:
|
|
|
437
441
|
|
|
438
442
|
## Release to consumer PR
|
|
439
443
|
|
|
444
|
+
This producer's release identity inputs and text assets declared by
|
|
445
|
+
`tool.setuptools.data-files` use UTF-8 without BOM and LF, matching `.gitattributes`.
|
|
446
|
+
Writers select that representation explicitly; JSON writers use
|
|
447
|
+
`write_json_atomic`. Existing distribution verification checks the declared text
|
|
448
|
+
bytes before building. It rejects CRLF, mixed endings, BOM and invalid UTF-8 even
|
|
449
|
+
when text-mode reads or Git conversion would hide the difference. Binary data and
|
|
450
|
+
fixtures outside that text inventory retain their own format contracts.
|
|
451
|
+
|
|
440
452
|
Every opted-in consumer uses Renovate's pip-compile manager. Its engineering-process
|
|
441
453
|
package rule keeps the adoption pull request in draft and runs exactly:
|
|
442
454
|
|
|
@@ -328,6 +328,10 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
328
328
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
329
329
|
the authority that started the relevant phase.
|
|
330
330
|
|
|
331
|
+
The [finding priority definitions](process_assets/skills/review-change/SKILL.md#finding-priority)
|
|
332
|
+
are the canonical P0-P3 impact convention for this process, including examples and
|
|
333
|
+
their relationship to blocking decisions.
|
|
334
|
+
|
|
331
335
|
## Running a change
|
|
332
336
|
|
|
333
337
|
Create and validate a change contract, then register it:
|
|
@@ -413,6 +417,14 @@ At any point:
|
|
|
413
417
|
|
|
414
418
|
## Release to consumer PR
|
|
415
419
|
|
|
420
|
+
This producer's release identity inputs and text assets declared by
|
|
421
|
+
`tool.setuptools.data-files` use UTF-8 without BOM and LF, matching `.gitattributes`.
|
|
422
|
+
Writers select that representation explicitly; JSON writers use
|
|
423
|
+
`write_json_atomic`. Existing distribution verification checks the declared text
|
|
424
|
+
bytes before building. It rejects CRLF, mixed endings, BOM and invalid UTF-8 even
|
|
425
|
+
when text-mode reads or Git conversion would hide the difference. Binary data and
|
|
426
|
+
fixtures outside that text inventory retain their own format contracts.
|
|
427
|
+
|
|
416
428
|
Every opted-in consumer uses Renovate's pip-compile manager. Its engineering-process
|
|
417
429
|
package rule keeps the adoption pull request in draft and runs exactly:
|
|
418
430
|
|
|
@@ -98,7 +98,12 @@ def validate_review_assessments(review: dict[str, Any], process_root: Path) -> N
|
|
|
98
98
|
unlinked = sorted(
|
|
99
99
|
finding_id
|
|
100
100
|
for finding_id, finding in findings.items()
|
|
101
|
-
if finding["origin"] == "production-invariant"
|
|
101
|
+
if finding["origin"] == "production-invariant"
|
|
102
|
+
and finding_id not in linked
|
|
103
|
+
and not (
|
|
104
|
+
finding["severity"] == "non-blocking"
|
|
105
|
+
and finding.get("disposition", {}).get("status") == "resolved"
|
|
106
|
+
)
|
|
102
107
|
)
|
|
103
108
|
if unlinked:
|
|
104
109
|
raise ProcessError(
|
{engineering_process-1.2.3 → engineering_process-1.2.5/engineering_process.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
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
|
|
@@ -352,6 +352,10 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
352
352
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
353
353
|
the authority that started the relevant phase.
|
|
354
354
|
|
|
355
|
+
The [finding priority definitions](process_assets/skills/review-change/SKILL.md#finding-priority)
|
|
356
|
+
are the canonical P0-P3 impact convention for this process, including examples and
|
|
357
|
+
their relationship to blocking decisions.
|
|
358
|
+
|
|
355
359
|
## Running a change
|
|
356
360
|
|
|
357
361
|
Create and validate a change contract, then register it:
|
|
@@ -437,6 +441,14 @@ At any point:
|
|
|
437
441
|
|
|
438
442
|
## Release to consumer PR
|
|
439
443
|
|
|
444
|
+
This producer's release identity inputs and text assets declared by
|
|
445
|
+
`tool.setuptools.data-files` use UTF-8 without BOM and LF, matching `.gitattributes`.
|
|
446
|
+
Writers select that representation explicitly; JSON writers use
|
|
447
|
+
`write_json_atomic`. Existing distribution verification checks the declared text
|
|
448
|
+
bytes before building. It rejects CRLF, mixed endings, BOM and invalid UTF-8 even
|
|
449
|
+
when text-mode reads or Git conversion would hide the difference. Binary data and
|
|
450
|
+
fixtures outside that text inventory retain their own format contracts.
|
|
451
|
+
|
|
440
452
|
Every opted-in consumer uses Renovate's pip-compile manager. Its engineering-process
|
|
441
453
|
package rule keeps the adoption pull request in draft and runs exactly:
|
|
442
454
|
|
|
@@ -29,6 +29,11 @@ Verification proves only the deterministic properties exercised by its exact
|
|
|
29
29
|
commands on the unchanged candidate. It must not claim to infer architecture,
|
|
30
30
|
security, intent, or arbitrary prose semantics that those commands do not observe.
|
|
31
31
|
|
|
32
|
+
When an artifact claim depends on byte identity, make encoding and newline rules
|
|
33
|
+
explicit at its writer and compare bytes at the exported or installed boundary.
|
|
34
|
+
Text-mode newline normalization and a clean Git diff cannot supply that proof;
|
|
35
|
+
follow the artifact owner's format rather than imposing one text policy on consumers.
|
|
36
|
+
|
|
32
37
|
Independent review reassesses every invariant against the contract, plan, complete
|
|
33
38
|
diff, profile evidence, and readiness result. Mark it `satisfied`, `not-applicable`,
|
|
34
39
|
or `violated`, give a concrete rationale, and cite evidence for `satisfied`. A
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/review-change/SKILL.md
RENAMED
|
@@ -62,3 +62,28 @@ Follow-up scope is only carried findings, remediation diffs, and regressions aga
|
|
|
62
62
|
the frozen contract. A new blocker must be either remediation-caused or a P0/P1 late
|
|
63
63
|
violation with a rationale. After two correction cycles, another changes-requested
|
|
64
64
|
verdict blocks the change; it never turns into approval or permission to skip review.
|
|
65
|
+
|
|
66
|
+
## Finding priority
|
|
67
|
+
|
|
68
|
+
P0-P3 are this process's review-impact convention. Assess the consequences if the
|
|
69
|
+
finding remains unresolved in a supported scenario. Consumer scheduling and incident
|
|
70
|
+
response deadlines remain consumer-owned.
|
|
71
|
+
|
|
72
|
+
| Priority | Impact | Illustrative example |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| P0 - critical | Catastrophic loss of essential operation, authoritative data, or a critical security guarantee. | Irrecoverable loss of the only authoritative dataset. |
|
|
75
|
+
| P1 - high | Severe failure of a core workflow or guarantee, below P0's catastrophic consequences. | A primary workflow produces wrong results that can be regenerated from intact inputs. |
|
|
76
|
+
| P2 - medium | Material but contained incorrect behavior or operational or maintenance cost; core workflows remain usable. | A recoverable failure in a secondary workflow with a safe alternative. |
|
|
77
|
+
| P3 - low | Minor inconvenience or clarity or consistency defect with little effect on behavior or reliability. | An unclear optional explanation when all required steps remain unambiguous. |
|
|
78
|
+
|
|
79
|
+
Choose the highest level whose consequences the evidence supports. Explain affected
|
|
80
|
+
guarantees, reach, preconditions, recoverability, and safe alternatives. Examples are
|
|
81
|
+
illustrative, not an exhaustive classifier; file type, finding category, keywords,
|
|
82
|
+
and ease of repair do not determine priority.
|
|
83
|
+
|
|
84
|
+
Severity follows whether the reviewed snapshot violates the accepted contract or an
|
|
85
|
+
applicable production invariant; priority is not a waiver. A P2 or P3 finding can
|
|
86
|
+
therefore be blocking. The existing P0/P1 restriction on a new `critical-late` blocker
|
|
87
|
+
is an eligibility condition, not permission to block without the required bounded
|
|
88
|
+
late rationale. Preserve carried finding priorities and historical reports under the
|
|
89
|
+
existing correction-cycle rules.
|
|
@@ -599,6 +599,43 @@ class LifecycleTests(unittest.TestCase):
|
|
|
599
599
|
self.assertEqual("implementing", state["phase"])
|
|
600
600
|
self.assertEqual({}, state["verification"])
|
|
601
601
|
|
|
602
|
+
def test_resolved_invariant_finishes_with_its_identity_and_history_preserved(self) -> None:
|
|
603
|
+
self.begin()
|
|
604
|
+
self.verify_all()
|
|
605
|
+
start_review(self.root, PROCESS_ROOT, "sample-change", actor_id="reviewer", context_id="review-context", kind="agent")
|
|
606
|
+
first = self.review_document("changes-requested")
|
|
607
|
+
first["findings"][0]["priority"] = "P2"
|
|
608
|
+
first_path = self.root / ".process" / "runs" / "first-review.json"
|
|
609
|
+
write_json(first_path, first)
|
|
610
|
+
state = submit_review(self.root, PROCESS_ROOT, "sample-change", first_path)
|
|
611
|
+
self.assertEqual("changes-requested", state["phase"])
|
|
612
|
+
|
|
613
|
+
begin_implementation(self.root, PROCESS_ROOT, "sample-change", actor_id="implementer", context_id="implementation-context-2", kind="agent")
|
|
614
|
+
(self.root / "product.txt").write_text("corrected\n", encoding="utf-8")
|
|
615
|
+
self.verify_all()
|
|
616
|
+
start_review(self.root, PROCESS_ROOT, "sample-change", actor_id="reviewer", context_id="review-context", kind="agent")
|
|
617
|
+
approved = self.review_document("approved")
|
|
618
|
+
carried = deepcopy(first["findings"][0])
|
|
619
|
+
carried["severity"] = "non-blocking"
|
|
620
|
+
carried["disposition"] = {"status": "resolved", "rationale": "The corrected candidate satisfies the invariant with fresh verification."}
|
|
621
|
+
approved["findings"] = [carried]
|
|
622
|
+
second_path = self.root / ".process" / "runs" / "second-review.json"
|
|
623
|
+
for field, changed in (("priority", "P3"), ("origin", "contract")):
|
|
624
|
+
invalid = deepcopy(approved)
|
|
625
|
+
invalid["findings"][0][field] = changed
|
|
626
|
+
write_json(second_path, invalid)
|
|
627
|
+
with self.subTest(field=field), self.assertRaisesRegex(ProcessError, "identity fields are immutable"):
|
|
628
|
+
submit_review(self.root, PROCESS_ROOT, "sample-change", second_path)
|
|
629
|
+
write_json(second_path, approved)
|
|
630
|
+
state = submit_review(self.root, PROCESS_ROOT, "sample-change", second_path)
|
|
631
|
+
self.assertEqual("approved", state["phase"])
|
|
632
|
+
self.assertEqual(first, state["reviewHistory"][0]["document"])
|
|
633
|
+
self.assertEqual(approved, state["reviewHistory"][1]["document"])
|
|
634
|
+
state, receipt = finish_change(self.root, PROCESS_ROOT, "sample-change", actor_id="coordinator", context_id="finish-context", kind="agent")
|
|
635
|
+
self.assertEqual("completed", state["phase"])
|
|
636
|
+
self.assertEqual(2, state["cycle"])
|
|
637
|
+
self.assertEqual("approved", receipt["review"]["verdict"])
|
|
638
|
+
|
|
602
639
|
def test_correction_review_requires_the_original_reviewer_identity(self) -> None:
|
|
603
640
|
self.begin()
|
|
604
641
|
self.verify_all()
|
|
@@ -793,6 +830,34 @@ class LifecycleTests(unittest.TestCase):
|
|
|
793
830
|
(self.root / ".process" / "runs" / "sample-change").exists()
|
|
794
831
|
)
|
|
795
832
|
|
|
833
|
+
def test_live_producer_policy_requires_its_accepted_issue(self) -> None:
|
|
834
|
+
project = json.loads((PROCESS_ROOT / ".process" / "project.json").read_text())
|
|
835
|
+
self.project["lifecycle"]["processChanges"] = project["lifecycle"]["processChanges"]
|
|
836
|
+
self.contract["consumerEvidence"] = [
|
|
837
|
+
{"repository": "engineering-process", "incident": "Deferred issue handoff enablement."}
|
|
838
|
+
]
|
|
839
|
+
for source in (
|
|
840
|
+
"issue-110",
|
|
841
|
+
"https://github.com/other/process/issues/110",
|
|
842
|
+
"https://github.com/phuongnse/engineering-process/pull/118",
|
|
843
|
+
):
|
|
844
|
+
with self.subTest(source=source):
|
|
845
|
+
self.contract["source"] = source
|
|
846
|
+
write_json(self.contract_path, self.contract)
|
|
847
|
+
with self.assertRaisesRegex(ProcessError, "numbered issue"):
|
|
848
|
+
start_change(
|
|
849
|
+
self.root, PROCESS_ROOT, self.project, self.contract_path,
|
|
850
|
+
actor_id="author", context_id="author-context", kind="agent",
|
|
851
|
+
)
|
|
852
|
+
self.assertFalse((self.root / ".process" / "runs").exists())
|
|
853
|
+
self.contract["source"] = "https://github.com/phuongnse/engineering-process/issues/110"
|
|
854
|
+
write_json(self.contract_path, self.contract)
|
|
855
|
+
state = start_change(
|
|
856
|
+
self.root, PROCESS_ROOT, self.project, self.contract_path,
|
|
857
|
+
actor_id="author", context_id="author-context", kind="agent",
|
|
858
|
+
)
|
|
859
|
+
self.assertEqual("specified", state["phase"])
|
|
860
|
+
|
|
796
861
|
def test_evidence_only_process_change_policy_remains_compatible(self) -> None:
|
|
797
862
|
self.project["lifecycle"]["processChanges"] = {
|
|
798
863
|
"requireConsumerEvidence": True
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/tests/test_production_engineering.py
RENAMED
|
@@ -190,6 +190,31 @@ class ProductionEngineeringTests(unittest.TestCase):
|
|
|
190
190
|
with self.assertRaisesRegex(ProcessError, "blocking finding"):
|
|
191
191
|
validate_review_assessments(violation, ROOT)
|
|
192
192
|
|
|
193
|
+
def test_resolved_invariant_findings_do_not_require_a_current_violation(self) -> None:
|
|
194
|
+
review = deepcopy(self.review)
|
|
195
|
+
review["findings"] = [{
|
|
196
|
+
"id": "resolved-invariant",
|
|
197
|
+
"severity": "non-blocking",
|
|
198
|
+
"priority": "P2",
|
|
199
|
+
"criterionId": "works",
|
|
200
|
+
"origin": "production-invariant",
|
|
201
|
+
"summary": "The previous invariant violation is resolved on this snapshot.",
|
|
202
|
+
"disposition": {"status": "resolved", "rationale": "The corrected structure and current evidence satisfy the invariant."},
|
|
203
|
+
}]
|
|
204
|
+
validate_document(review, "review", schema_root=SCHEMAS)
|
|
205
|
+
validate_review_assessments(review, ROOT)
|
|
206
|
+
for status in ("accepted-risk", "tracked-follow-up"):
|
|
207
|
+
with self.subTest(status=status):
|
|
208
|
+
review["findings"][0]["disposition"] = {
|
|
209
|
+
"status": status,
|
|
210
|
+
"rationale": "The invariant violation remains open.",
|
|
211
|
+
"owner": "consumer-owner",
|
|
212
|
+
"recordUrl": "https://example.invalid/issues/1",
|
|
213
|
+
}
|
|
214
|
+
validate_document(review, "review", schema_root=SCHEMAS)
|
|
215
|
+
with self.assertRaisesRegex(ProcessError, "must be linked"):
|
|
216
|
+
validate_review_assessments(review, ROOT)
|
|
217
|
+
|
|
193
218
|
def test_review_cannot_approve_a_violation_or_leave_invariant_finding_unlinked(self) -> None:
|
|
194
219
|
violation = deepcopy(self.review)
|
|
195
220
|
assessment = violation["productionEngineering"][0]
|
|
@@ -12,12 +12,54 @@ import unittest
|
|
|
12
12
|
from engineering_process.contracts import ProcessError, read_json, write_json_atomic
|
|
13
13
|
from engineering_process.release import derive_next_version, validate_release
|
|
14
14
|
from verification.normalize_sdist import normalize
|
|
15
|
+
from verification.prepare_release import _replace_once
|
|
16
|
+
from verification.verify_distribution import validate_distribution_text
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
ROOT = Path(__file__).resolve().parent.parent
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class ReleaseTests(unittest.TestCase):
|
|
23
|
+
def test_release_replacement_writes_explicit_lf_on_every_host(self) -> None:
|
|
24
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
25
|
+
target = Path(directory) / "version.txt"
|
|
26
|
+
for source in (b"version=old\nnext\n", b"version=old\r\nnext\r\n", b"version=old\r\nnext\n"):
|
|
27
|
+
with self.subTest(source=source):
|
|
28
|
+
target.write_bytes(source)
|
|
29
|
+
_replace_once(target, "old", "new")
|
|
30
|
+
self.assertEqual(b"version=new\nnext\n", target.read_bytes())
|
|
31
|
+
|
|
32
|
+
def test_release_replacement_rejects_bom_before_writing(self) -> None:
|
|
33
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
34
|
+
target = Path(directory) / "version.txt"
|
|
35
|
+
original = b'\xef\xbb\xbfVERSION = "old"\r\n'
|
|
36
|
+
target.write_bytes(original)
|
|
37
|
+
with self.assertRaisesRegex(ProcessError, "UTF-8 without BOM"):
|
|
38
|
+
_replace_once(target, "old", "new")
|
|
39
|
+
self.assertEqual(original, target.read_bytes())
|
|
40
|
+
self.assertFalse(target.with_name(f".{target.name}.release.tmp").exists())
|
|
41
|
+
|
|
42
|
+
def test_distribution_text_uses_the_declared_inventory_and_rejects_byte_drift(self) -> None:
|
|
43
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
44
|
+
root = Path(directory)
|
|
45
|
+
(root / "engineering_process").mkdir()
|
|
46
|
+
(root / "assets").mkdir()
|
|
47
|
+
(root / "pyproject.toml").write_bytes(b'[tool.setuptools.data-files]\n"share/process" = ["assets/newly-declared.fragment"]\n')
|
|
48
|
+
(root / "release.json").write_bytes(b"{}\n")
|
|
49
|
+
(root / "engineering_process" / "__init__.py").write_bytes(b'VERSION = "1.0.0"\n')
|
|
50
|
+
(root / "opaque.bin").write_bytes(b"\x00\xff\r\n")
|
|
51
|
+
target = root / "assets" / "newly-declared.fragment"
|
|
52
|
+
valid = b"first\nsecond\n"
|
|
53
|
+
target.write_bytes(valid)
|
|
54
|
+
validate_distribution_text(root)
|
|
55
|
+
for invalid in (b"first\r\nsecond\r\n", b"first\r\nsecond\n", b"\xef\xbb\xbf" + valid, b"\xff\n"):
|
|
56
|
+
with self.subTest(invalid=invalid):
|
|
57
|
+
target.write_bytes(invalid)
|
|
58
|
+
with self.assertRaisesRegex(RuntimeError, "UTF-8 without BOM and use LF"):
|
|
59
|
+
validate_distribution_text(root)
|
|
60
|
+
target.write_bytes(valid)
|
|
61
|
+
validate_distribution_text(root)
|
|
62
|
+
|
|
21
63
|
def test_current_release_identity_is_consistent(self) -> None:
|
|
22
64
|
release = read_json(ROOT / "release.json")
|
|
23
65
|
result = validate_release(ROOT, ROOT, tag=f"v{release['version']}")
|
|
@@ -90,6 +132,7 @@ class ReleaseTests(unittest.TestCase):
|
|
|
90
132
|
self.assertIn(
|
|
91
133
|
f'version = "{expected}"', (target / "pyproject.toml").read_text()
|
|
92
134
|
)
|
|
135
|
+
validate_distribution_text(target)
|
|
93
136
|
|
|
94
137
|
def test_invalid_change_type_fails(self) -> None:
|
|
95
138
|
with self.assertRaises(ProcessError):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_contract.py
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_posix.py
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/_supervisor_windows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/helper_launch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/publication_compat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/requirements-dev.txt
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process/requirements-runtime.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/requires.txt
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/engineering_process.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/finish-change/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/plan-change/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/run-change/SKILL.md
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/start-change/SKILL.md
RENAMED
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/process_assets/skills/verify-change/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/schemas/production-engineering.schema.json
RENAMED
|
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
|
{engineering_process-1.2.3 → engineering_process-1.2.5}/templates/adopt-process-windows-job.py
RENAMED
|
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
|