engineering-process 0.2.1__tar.gz → 0.4.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.
- {engineering_process-0.2.1/engineering_process.egg-info → engineering_process-0.4.0}/PKG-INFO +36 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/PRODUCTION_STANDARD.md +18 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/README.md +35 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/VERSIONING.md +14 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/__init__.py +1 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_supervisor_posix.py +15 -2
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/lifecycle.py +36 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/supervision.py +3 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0/engineering_process.egg-info}/PKG-INFO +36 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/SOURCES.txt +1 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/evolve-process/SKILL.md +13 -5
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/review-change/SKILL.md +3 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-change/SKILL.md +6 -2
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-change/references/execution.md +31 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/pyproject.toml +1 -1
- engineering_process-0.4.0/release.json +55 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_environment.py +34 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_lifecycle.py +62 -0
- engineering_process-0.4.0/tests/test_process_install.py +235 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_self_hosting.py +36 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_skills.py +21 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_supervision.py +62 -1
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_windows_job.py +7 -0
- engineering_process-0.2.1/release.json +0 -106
- {engineering_process-0.2.1 → engineering_process-0.4.0}/LICENSE +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/MANIFEST.in +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/bundles.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/__main__.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_download_worker.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_supervisor_windows.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_windows_job.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/adoption.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/artifact_attestation.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/bootstrap.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/bundles.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/cli.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/contracts.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/distribution.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/distribution_verify.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/environment.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/evidence.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/git.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/git_attributes.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/helper_launch.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/impact.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/managed.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/markdown.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/publication.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/release.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/release_candidate.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-build.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-dev.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-release.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-runtime.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/runner.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/runtime.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/skills.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/supplemental.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/syncing.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/tooling.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/dependency_links.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/entry_points.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/requires.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/top_level.txt +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/adoption-migration.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/change.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/plan.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/project.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/release-change.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/release.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/review.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/assess-design/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/build-frontend/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/build-frontend-foundation/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/change-api/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/cross-repo-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/define-change-contract/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/design-module/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/finish-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/govern-ui/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-module/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-use-case/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/integrate-mcp/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/maintain-docs/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/plan-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/publish-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-project-command/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/specify-use-case/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/verify-change/SKILL.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/adoption-migration.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/change.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/evidence-receipt.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/lifecycle.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/plan.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/process-lock.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/project.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/release-change.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/release.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/review.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/reviewer-attestation.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/supplemental-verification.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/verification.schema.json +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/setup.cfg +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/AGENTS.process.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/adopt-process-windows-job.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/adopt-process.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_adoption.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_adoption_runner.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_artifact_attestation.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_bootstrap.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_bundles.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_cli.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_contracts.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_distribution.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_distribution_verify.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_evals.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_impact.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_publication.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_publish_event.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_pypi_publication.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_candidate.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_preparation.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_qualification.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_runner.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_runtime.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_schemas.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_source_checkout.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_supplemental.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_syncing.py +0 -0
- {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_tooling.py +0 -0
{engineering_process-0.2.1/engineering_process.egg-info → engineering_process-0.4.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Agent-neutral end-to-end engineering lifecycle and deterministic process CLI
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/phuongnse/engineering-process
|
|
@@ -85,6 +85,9 @@ distribution: the POSIX backend owns a new process session/group and the Windows
|
|
|
85
85
|
backend owns a kill-on-close Job Object. Consumer manifests, evidence, and exit codes
|
|
86
86
|
do not branch by operating system. If an outer Windows Job applies incompatible
|
|
87
87
|
nesting or UI limits, target creation fails closed instead of running uncontained.
|
|
88
|
+
After a command root exits, both backends allow at most 250 milliseconds for child
|
|
89
|
+
accounting to drain naturally. A process still present after that bound is terminated
|
|
90
|
+
and makes the command fail; commands with no remaining child return immediately.
|
|
88
91
|
|
|
89
92
|
This task boundary intentionally separates finite commands from services and
|
|
90
93
|
interactive protocols. `processctl exec`, requirement probes, setup command actions,
|
|
@@ -136,6 +139,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
136
139
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
137
140
|
attestation. A consumer never authors or maintains process skills locally.
|
|
138
141
|
|
|
142
|
+
CI installs the pinned authority through the repository-root
|
|
143
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
144
|
+
the action with the release commit's full object id and retain the human-readable
|
|
145
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
146
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
147
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
148
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
149
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
150
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
151
|
+
replace the installer.
|
|
152
|
+
|
|
153
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
154
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
155
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
156
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
157
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
158
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
159
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
160
|
+
distribution.
|
|
161
|
+
|
|
139
162
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
140
163
|
The managed runner installs the target authority from the complete hash lock outside
|
|
141
164
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -386,6 +409,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
386
409
|
--actor worker --context worker-session --actor-kind agent
|
|
387
410
|
~~~
|
|
388
411
|
|
|
412
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
413
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
414
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
415
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
416
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
417
|
+
to bypass independent review.
|
|
418
|
+
|
|
389
419
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
390
420
|
assignment:
|
|
391
421
|
|
|
@@ -504,6 +534,11 @@ authenticates who produced it.
|
|
|
504
534
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
505
535
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
506
536
|
independent review, merge is the end of adoption.
|
|
537
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
538
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
539
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
540
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
541
|
+
executes helper source from the consumer branch.
|
|
507
542
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
508
543
|
completion-related artifacts, release-change fragments, and the release
|
|
509
544
|
classification contract. The generated Release PR gate binds that contract to the
|
|
@@ -59,6 +59,24 @@ its schema-2 profile reports. The remote artifact id and service-computed digest
|
|
|
59
59
|
preserved with review evidence. These reports supplement the public N-1 lifecycle
|
|
60
60
|
authority; code under verification never promotes itself to lifecycle authority.
|
|
61
61
|
|
|
62
|
+
## Failure to invariant
|
|
63
|
+
|
|
64
|
+
A validated command, gate, release, adoption, or external-integration failure must
|
|
65
|
+
preserve exact bounded evidence and classify its owner before corrective mutation as
|
|
66
|
+
project-local, shared-process, operations-or-external, or missing product or
|
|
67
|
+
authorization input. Dependent candidates stay blocked. Shared defects are corrected
|
|
68
|
+
in the producer rather than wrapped or duplicated by consumers; project behavior
|
|
69
|
+
remains project-owned unless a portable class is proven.
|
|
70
|
+
|
|
71
|
+
Every correction proves valid behavior and the corresponding fail-closed class at the
|
|
72
|
+
lowest reliable owner boundary. Shared corrections additionally require producer
|
|
73
|
+
profiles and reproduction at affected consumer boundaries before release. A transient
|
|
74
|
+
operations/external recovery is allowed only with unchanged source and configuration;
|
|
75
|
+
attempts are bounded, idempotent, diagnostic-preserving, and stop on deterministic
|
|
76
|
+
failure. Source, branch, version, credentials, and controls are never changed merely
|
|
77
|
+
to cause another attempt. Independent review treats violation of these ownership and
|
|
78
|
+
evidence boundaries as completion-blocking.
|
|
79
|
+
|
|
62
80
|
## Resource and generated-state policy
|
|
63
81
|
|
|
64
82
|
Every operation over repository-controlled or remote input has explicit limits for
|
|
@@ -50,6 +50,9 @@ distribution: the POSIX backend owns a new process session/group and the Windows
|
|
|
50
50
|
backend owns a kill-on-close Job Object. Consumer manifests, evidence, and exit codes
|
|
51
51
|
do not branch by operating system. If an outer Windows Job applies incompatible
|
|
52
52
|
nesting or UI limits, target creation fails closed instead of running uncontained.
|
|
53
|
+
After a command root exits, both backends allow at most 250 milliseconds for child
|
|
54
|
+
accounting to drain naturally. A process still present after that bound is terminated
|
|
55
|
+
and makes the command fail; commands with no remaining child return immediately.
|
|
53
56
|
|
|
54
57
|
This task boundary intentionally separates finite commands from services and
|
|
55
58
|
interactive protocols. `processctl exec`, requirement probes, setup command actions,
|
|
@@ -101,6 +104,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
101
104
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
102
105
|
attestation. A consumer never authors or maintains process skills locally.
|
|
103
106
|
|
|
107
|
+
CI installs the pinned authority through the repository-root
|
|
108
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
109
|
+
the action with the release commit's full object id and retain the human-readable
|
|
110
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
111
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
112
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
113
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
114
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
115
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
116
|
+
replace the installer.
|
|
117
|
+
|
|
118
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
119
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
120
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
121
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
122
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
123
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
124
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
125
|
+
distribution.
|
|
126
|
+
|
|
104
127
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
105
128
|
The managed runner installs the target authority from the complete hash lock outside
|
|
106
129
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -351,6 +374,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
351
374
|
--actor worker --context worker-session --actor-kind agent
|
|
352
375
|
~~~
|
|
353
376
|
|
|
377
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
378
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
379
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
380
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
381
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
382
|
+
to bypass independent review.
|
|
383
|
+
|
|
354
384
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
355
385
|
assignment:
|
|
356
386
|
|
|
@@ -469,6 +499,11 @@ authenticates who produced it.
|
|
|
469
499
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
470
500
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
471
501
|
independent review, merge is the end of adoption.
|
|
502
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
503
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
504
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
505
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
506
|
+
executes helper source from the consumer branch.
|
|
472
507
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
473
508
|
completion-related artifacts, release-change fragments, and the release
|
|
474
509
|
classification contract. The generated Release PR gate binds that contract to the
|
|
@@ -98,6 +98,20 @@ Release, self-adoption, and consumer adoption are separate changes:
|
|
|
98
98
|
checkpoint. Merge completes adoption; there is no post-merge synchronization.
|
|
99
99
|
5. N+1 governs only changes that begin after the adoption checkpoint.
|
|
100
100
|
|
|
101
|
+
The repository-root GitHub Action and Python package are two surfaces of the same
|
|
102
|
+
governed release checkpoint. A consumer invocation pins the action by the release
|
|
103
|
+
commit's full object id and keeps the `v<SemVer>` annotation; Renovate groups that
|
|
104
|
+
GitHub Action identity with the direct Python authority update. The action does not
|
|
105
|
+
select the package version. It installs only the consumer's complete hash lock, so
|
|
106
|
+
the process lock, Python artifacts, managed assets, action source, and reviewed tag
|
|
107
|
+
remain independently checkable parts of one release identity.
|
|
108
|
+
|
|
109
|
+
Consumer repositories do not own copies of reusable installation or publication
|
|
110
|
+
algorithms. They retain declarative process/project locks, exact project commands,
|
|
111
|
+
and managed bootstrap snapshots required before a target version is installed. A
|
|
112
|
+
clean-cutover PR may delete an obsolete local helper only after it pins an immutable
|
|
113
|
+
public action checkpoint; no compatibility shim or dual execution path is retained.
|
|
114
|
+
|
|
101
115
|
Renovate PRs are generated adoption candidates, not trusted adoption evidence.
|
|
102
116
|
Automerge is forbidden for process-authority updates. A PR that changes only a
|
|
103
117
|
requirement pin, omits generated hashes or managed assets, or requires a post-merge
|
{engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_supervisor_posix.py
RENAMED
|
@@ -9,7 +9,7 @@ import subprocess
|
|
|
9
9
|
import time
|
|
10
10
|
from typing import Mapping
|
|
11
11
|
|
|
12
|
-
from .supervision import CleanupOutcome
|
|
12
|
+
from .supervision import CleanupOutcome, NATURAL_DRAIN_GRACE_MILLISECONDS
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _process_group_exists(process_group: int) -> bool:
|
|
@@ -156,7 +156,20 @@ class PosixProcessSupervisor:
|
|
|
156
156
|
grace_seconds: float,
|
|
157
157
|
) -> CleanupOutcome:
|
|
158
158
|
# The group id remains valid while any descendant from the owned session is
|
|
159
|
-
# alive, even after the original process has exited.
|
|
159
|
+
# alive, even after the original process has exited. Give descendants that
|
|
160
|
+
# were synchronously asked to stop one short bounded interval to disappear
|
|
161
|
+
# naturally before classifying them as abandoned background work.
|
|
162
|
+
if not _process_group_exists(process.pid):
|
|
163
|
+
return CleanupOutcome(bounded=True)
|
|
164
|
+
natural_drain_seconds = min(
|
|
165
|
+
grace_seconds,
|
|
166
|
+
NATURAL_DRAIN_GRACE_MILLISECONDS / 1000,
|
|
167
|
+
)
|
|
168
|
+
if natural_drain_seconds > 0 and _wait_for_process_group(
|
|
169
|
+
process.pid,
|
|
170
|
+
natural_drain_seconds,
|
|
171
|
+
):
|
|
172
|
+
return CleanupOutcome(bounded=True)
|
|
160
173
|
return _terminate_group(process.pid, grace_seconds)
|
|
161
174
|
|
|
162
175
|
|
|
@@ -624,10 +624,45 @@ def _begin_implementation_unlocked(
|
|
|
624
624
|
kind: str,
|
|
625
625
|
) -> dict[str, Any]:
|
|
626
626
|
state = load_state(project_root, change_id)
|
|
627
|
-
_require_phase(state, "planned", "implementing", "changes-requested")
|
|
628
627
|
_contract(project_root, state)
|
|
629
628
|
_plan(project_root, state)
|
|
630
629
|
actor = _actor(actor_id, context_id, kind)
|
|
630
|
+
if state["phase"] == "verified":
|
|
631
|
+
source = source_state(project_root)
|
|
632
|
+
checkpoints = {item["checkpoint"] for item in state["verification"]}
|
|
633
|
+
fingerprints = {
|
|
634
|
+
item["workspaceFingerprint"] for item in state["verification"]
|
|
635
|
+
}
|
|
636
|
+
current = (
|
|
637
|
+
source["dirty"] is False
|
|
638
|
+
and source["checkpoint"] is not None
|
|
639
|
+
and source["fingerprint"] is not None
|
|
640
|
+
and checkpoints == {source["checkpoint"]}
|
|
641
|
+
and fingerprints == {source["fingerprint"]}
|
|
642
|
+
)
|
|
643
|
+
if current:
|
|
644
|
+
raise ContractError(
|
|
645
|
+
"current verified change must enter independent review before "
|
|
646
|
+
"another implementation cycle"
|
|
647
|
+
)
|
|
648
|
+
previous_cycle = state["cycle"]
|
|
649
|
+
previous_verification = list(state["verification"])
|
|
650
|
+
state["cycle"] += 1
|
|
651
|
+
state["implementationActors"] = []
|
|
652
|
+
state["verification"] = []
|
|
653
|
+
state["reviewAssignment"] = None
|
|
654
|
+
state["review"] = None
|
|
655
|
+
_event(
|
|
656
|
+
state,
|
|
657
|
+
"verification-invalidated",
|
|
658
|
+
actor,
|
|
659
|
+
cycle=state["cycle"],
|
|
660
|
+
previousCycle=previous_cycle,
|
|
661
|
+
previousVerification=previous_verification,
|
|
662
|
+
reason="source-changed-after-verification",
|
|
663
|
+
)
|
|
664
|
+
else:
|
|
665
|
+
_require_phase(state, "planned", "implementing", "changes-requested")
|
|
631
666
|
if state["phase"] == "changes-requested":
|
|
632
667
|
state["cycle"] += 1
|
|
633
668
|
state["implementationActors"] = []
|
{engineering_process-0.2.1 → engineering_process-0.4.0/engineering_process.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Agent-neutral end-to-end engineering lifecycle and deterministic process CLI
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/phuongnse/engineering-process
|
|
@@ -85,6 +85,9 @@ distribution: the POSIX backend owns a new process session/group and the Windows
|
|
|
85
85
|
backend owns a kill-on-close Job Object. Consumer manifests, evidence, and exit codes
|
|
86
86
|
do not branch by operating system. If an outer Windows Job applies incompatible
|
|
87
87
|
nesting or UI limits, target creation fails closed instead of running uncontained.
|
|
88
|
+
After a command root exits, both backends allow at most 250 milliseconds for child
|
|
89
|
+
accounting to drain naturally. A process still present after that bound is terminated
|
|
90
|
+
and makes the command fail; commands with no remaining child return immediately.
|
|
88
91
|
|
|
89
92
|
This task boundary intentionally separates finite commands from services and
|
|
90
93
|
interactive protocols. `processctl exec`, requirement probes, setup command actions,
|
|
@@ -136,6 +139,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
136
139
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
137
140
|
attestation. A consumer never authors or maintains process skills locally.
|
|
138
141
|
|
|
142
|
+
CI installs the pinned authority through the repository-root
|
|
143
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
144
|
+
the action with the release commit's full object id and retain the human-readable
|
|
145
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
146
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
147
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
148
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
149
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
150
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
151
|
+
replace the installer.
|
|
152
|
+
|
|
153
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
154
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
155
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
156
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
157
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
158
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
159
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
160
|
+
distribution.
|
|
161
|
+
|
|
139
162
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
140
163
|
The managed runner installs the target authority from the complete hash lock outside
|
|
141
164
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -386,6 +409,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
386
409
|
--actor worker --context worker-session --actor-kind agent
|
|
387
410
|
~~~
|
|
388
411
|
|
|
412
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
413
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
414
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
415
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
416
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
417
|
+
to bypass independent review.
|
|
418
|
+
|
|
389
419
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
390
420
|
assignment:
|
|
391
421
|
|
|
@@ -504,6 +534,11 @@ authenticates who produced it.
|
|
|
504
534
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
505
535
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
506
536
|
independent review, merge is the end of adoption.
|
|
537
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
538
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
539
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
540
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
541
|
+
executes helper source from the consumer branch.
|
|
507
542
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
508
543
|
completion-related artifacts, release-change fragments, and the release
|
|
509
544
|
classification contract. The generated Release PR gate binds that contract to the
|
|
@@ -14,18 +14,26 @@ incident history into permanent ceremony.
|
|
|
14
14
|
|
|
15
15
|
1. Start from a reproducer, escaped defect, false positive, review finding, or measured
|
|
16
16
|
workflow cost. Do not generalize from speculation alone.
|
|
17
|
-
2.
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
2. Apply the run-change failure-to-invariant protocol. Classify the owning boundary as
|
|
18
|
+
project-local, shared-process, operations-or-external, or missing product or
|
|
19
|
+
authorization input before corrective mutation; then classify the reusable class
|
|
20
|
+
as a process rule, deterministic invariant, portability gap, or obsolete rule.
|
|
21
|
+
3. Fix local behavior in the project owner. When the defect belongs to shared process
|
|
22
|
+
semantics, keep the consumer candidate blocked and remove any provisional consumer
|
|
23
|
+
wrapper or duplicate implementation. Change shared skills only for portable
|
|
20
24
|
semantics. Add CLI or schema enforcement only when deterministic.
|
|
21
|
-
4. Add
|
|
22
|
-
|
|
25
|
+
4. Add regression cases at the lowest reliable owner boundary for both valid behavior
|
|
26
|
+
and the fail-closed class. A shared fix also requires producer profiles and a real
|
|
27
|
+
affected-consumer reproduction before release authorization. For skill behavior,
|
|
28
|
+
run a realistic forward test without providing the expected answer.
|
|
23
29
|
5. Validate backward compatibility, version impact, consumer locks, and migration
|
|
24
30
|
needs. Remove superseded guidance instead of preserving duplicate paths.
|
|
25
31
|
Check the full `production-v1` boundary and derive every release identity surface
|
|
26
32
|
from the release contract so a local fix cannot create a new cross-surface drift.
|
|
27
33
|
6. Measure whether the change improves task fidelity without disproportionate
|
|
28
34
|
workflow cost.
|
|
35
|
+
7. For an operations or external transient, require bounded idempotent recovery with
|
|
36
|
+
retained per-attempt diagnostics. Never mutate source or version merely to retry.
|
|
29
37
|
|
|
30
38
|
## Hard gates
|
|
31
39
|
|
{engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/review-change/SKILL.md
RENAMED
|
@@ -33,6 +33,9 @@ specific checkpoint without changing the reviewed source.
|
|
|
33
33
|
Resolved, deferred, and false-positive findings require resolution evidence.
|
|
34
34
|
Deferred remains unresolved and completion-blocking unless a future process
|
|
35
35
|
contract introduces an explicit owner-approved exception.
|
|
36
|
+
Treat owner mismatch, consumer workaround for a shared defect, evidence-free rerun,
|
|
37
|
+
missing valid/fail-closed regression proof, or missing affected-consumer proof for
|
|
38
|
+
a shared correction as required completion-blocking findings.
|
|
36
39
|
5. Request changes when any required finding remains open or deferred. Approve only
|
|
37
40
|
when required outcomes and evidence are complete for the reviewed checkpoint.
|
|
38
41
|
6. Validate the report with processctl contract validate --kind review, then submit
|
{engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-change/SKILL.md
RENAMED
|
@@ -31,10 +31,14 @@ semantics shared by every project.
|
|
|
31
31
|
3. Apply the nearest AGENTS.md and domain skill inside each phase. Project policy may
|
|
32
32
|
add stronger gates but cannot remove lifecycle phases, baseline profiles,
|
|
33
33
|
independent review, evidence freshness, or finding closure.
|
|
34
|
-
4.
|
|
34
|
+
4. When a command, gate, release, adoption, or external integration fails, apply the
|
|
35
|
+
failure-to-invariant protocol in the required execution reference before any
|
|
36
|
+
corrective mutation. Keep dependent candidates blocked until the owning boundary
|
|
37
|
+
and required proof are explicit.
|
|
38
|
+
5. After a valid finding, preserve the reviewed checkpoint, begin the next
|
|
35
39
|
implementation cycle, resolve the finding, and repeat every invalidated profile
|
|
36
40
|
and independent review.
|
|
37
|
-
|
|
41
|
+
6. Report the processctl phase, cycle, current evidence, blockers, and next owner.
|
|
38
42
|
Never call a task complete from prose alone.
|
|
39
43
|
|
|
40
44
|
## Hard gates
|
|
@@ -54,6 +54,37 @@ When progress depends on user-controlled or external state:
|
|
|
54
54
|
5. Report `Blocker`, `Evidence`, `Boundary`, `User action or decision needed`, and
|
|
55
55
|
`Safe next step after confirmation`.
|
|
56
56
|
|
|
57
|
+
## Failure-to-invariant protocol
|
|
58
|
+
|
|
59
|
+
Apply this protocol before corrective mutation whenever a command, gate, release,
|
|
60
|
+
adoption, or external integration produces a validated failure:
|
|
61
|
+
|
|
62
|
+
1. Preserve the smallest reliable reproducer, exact command/event, immutable source
|
|
63
|
+
identity, environment, exit status, bounded output, and available service evidence.
|
|
64
|
+
Do not use an evidence-free rerun as diagnosis.
|
|
65
|
+
2. Classify the owning boundary as `project-local`, `shared-process`,
|
|
66
|
+
`operations-or-external`, or `missing-product-or-authorization-input`. State the
|
|
67
|
+
evidence that excludes the other boundaries before selecting a fix.
|
|
68
|
+
3. Keep every dependent candidate blocked. A shared-process defect must be fixed in
|
|
69
|
+
the shared producer; do not add a consumer-owned wrapper, duplicate algorithm,
|
|
70
|
+
alternate authority, relaxed control, or environment substitution to keep moving.
|
|
71
|
+
A project-local behavior remains in the project owner and must not be promoted to
|
|
72
|
+
portable core without evidence of a reusable class.
|
|
73
|
+
4. Add regression evidence at the lowest reliable owner boundary for both the valid
|
|
74
|
+
behavior and the corresponding persistent, invalid, timeout, or interruption case
|
|
75
|
+
that must remain fail closed.
|
|
76
|
+
5. For a shared correction, require producer profiles and a reproduction at every
|
|
77
|
+
affected consumer boundary before release authorization. Consumer proof supplements
|
|
78
|
+
producer evidence; neither substitutes for the other.
|
|
79
|
+
6. Treat operations or external propagation as transient only when source and
|
|
80
|
+
configuration are already proven unchanged. Recovery must be bounded, idempotent,
|
|
81
|
+
preserve per-attempt diagnostics, and stop on a deterministic failure. Do not
|
|
82
|
+
change source, branch, version, credentials, or controls merely to cause another
|
|
83
|
+
attempt.
|
|
84
|
+
7. Reopen or create the owning change lifecycle when scope moves across a boundary,
|
|
85
|
+
then repeat invalidated verification and independent review on one exact final
|
|
86
|
+
checkpoint. Record the reusable invariant, not just the incident chronology.
|
|
87
|
+
|
|
57
88
|
## Independent review
|
|
58
89
|
|
|
59
90
|
Review begins only after all baseline and change-required profiles pass on one clean
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 3,
|
|
3
|
+
"previousVersion": "0.3.0",
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"classification": "minor",
|
|
6
|
+
"compatibility": "backward-compatible",
|
|
7
|
+
"schemaImpact": "unchanged",
|
|
8
|
+
"migration": null,
|
|
9
|
+
"identity": {
|
|
10
|
+
"package": "engineering-process",
|
|
11
|
+
"distribution": "engineering_process",
|
|
12
|
+
"tag": "v0.4.0",
|
|
13
|
+
"releaseName": "v0.4.0",
|
|
14
|
+
"runtimeVersion": {
|
|
15
|
+
"path": "engineering_process/__init__.py",
|
|
16
|
+
"variable": "VERSION"
|
|
17
|
+
},
|
|
18
|
+
"artifacts": [
|
|
19
|
+
"engineering_process-0.4.0-py3-none-any.whl",
|
|
20
|
+
"engineering_process-0.4.0.tar.gz"
|
|
21
|
+
],
|
|
22
|
+
"receiptAsset": "engineering-process-v0.4.0-evidence.json",
|
|
23
|
+
"authorizationAsset": null
|
|
24
|
+
},
|
|
25
|
+
"provenance": {
|
|
26
|
+
"mode": "governed",
|
|
27
|
+
"statement": "The public N-1 lifecycle receipt and reviewed Release PR authorize this release.",
|
|
28
|
+
"lifecycleReceipt": {
|
|
29
|
+
"asset": "engineering-process-v0.4.0-evidence.json",
|
|
30
|
+
"project": "engineering-process",
|
|
31
|
+
"changeId": "release-0-4-0",
|
|
32
|
+
"cycle": 1
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"changes": [
|
|
36
|
+
{
|
|
37
|
+
"id": "failure-to-invariant",
|
|
38
|
+
"type": "capability",
|
|
39
|
+
"surfaces": [
|
|
40
|
+
"governance",
|
|
41
|
+
"skills"
|
|
42
|
+
],
|
|
43
|
+
"rationale": "Require future failures to preserve evidence, classify the correct owner, repair a reusable invariant, prove valid and fail-closed behavior, and obtain affected-consumer proof before shared release."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "natural-descendant-drain",
|
|
47
|
+
"type": "fix",
|
|
48
|
+
"surfaces": [
|
|
49
|
+
"execution",
|
|
50
|
+
"supervision"
|
|
51
|
+
],
|
|
52
|
+
"rationale": "Allow short, bounded child-process shutdown to drain naturally on POSIX just as it already does under the Windows Job Object, while persistent descendants remain terminated and completion-blocking."
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -101,6 +101,40 @@ def project_document(*, setup: bool = True, dependency: bool = False):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class EnvironmentTests(unittest.TestCase):
|
|
104
|
+
@unittest.skipUnless(os.name == "posix", "POSIX process-group regression")
|
|
105
|
+
def test_successful_command_allows_short_natural_descendant_drain(self):
|
|
106
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
107
|
+
root = Path(directory)
|
|
108
|
+
child_started = root / "child.started"
|
|
109
|
+
child_code = (
|
|
110
|
+
"from pathlib import Path; import sys, time; "
|
|
111
|
+
"Path(sys.argv[1]).write_text('started'); time.sleep(0.08)"
|
|
112
|
+
)
|
|
113
|
+
parent_code = (
|
|
114
|
+
"import subprocess, sys, time; from pathlib import Path; "
|
|
115
|
+
"subprocess.Popen([sys.executable, '-c', sys.argv[1], sys.argv[2]]); "
|
|
116
|
+
"marker=Path(sys.argv[2]); deadline=time.monotonic()+5; "
|
|
117
|
+
"\nwhile not marker.exists() and time.monotonic() < deadline: time.sleep(0.01)\n"
|
|
118
|
+
"raise SystemExit(0 if marker.exists() else 2)"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
report = execute_command(
|
|
122
|
+
root,
|
|
123
|
+
identifier="natural-drain",
|
|
124
|
+
run=(
|
|
125
|
+
sys.executable,
|
|
126
|
+
"-c",
|
|
127
|
+
parent_code,
|
|
128
|
+
child_code,
|
|
129
|
+
str(child_started),
|
|
130
|
+
),
|
|
131
|
+
timeout_seconds=10,
|
|
132
|
+
working_directory=".",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
self.assertEqual("passed", report["status"])
|
|
136
|
+
self.assertEqual(0, report["exitCode"])
|
|
137
|
+
|
|
104
138
|
def test_managed_command_binding_preserves_logical_evidence_without_a_shell(self):
|
|
105
139
|
with tempfile.TemporaryDirectory() as directory:
|
|
106
140
|
root = Path(directory)
|
|
@@ -300,6 +300,68 @@ class LifecycleTests(unittest.TestCase):
|
|
|
300
300
|
self.assertEqual(report["impact"]["baseRef"], "HEAD")
|
|
301
301
|
self.assertEqual(report["impact"]["changedPaths"], [])
|
|
302
302
|
|
|
303
|
+
def test_stale_verified_source_can_begin_a_new_implementation_cycle(self):
|
|
304
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
305
|
+
base = Path(directory)
|
|
306
|
+
root = base / "project"
|
|
307
|
+
inputs = base / "inputs"
|
|
308
|
+
root.mkdir()
|
|
309
|
+
inputs.mkdir()
|
|
310
|
+
self.initialize_repository(root)
|
|
311
|
+
self.prepare_verified_change(root, inputs)
|
|
312
|
+
|
|
313
|
+
(root / "tracked.txt").write_text("remote CI correction\n", encoding="utf-8")
|
|
314
|
+
subprocess.run(["git", "add", "tracked.txt"], cwd=root, check=True)
|
|
315
|
+
subprocess.run(
|
|
316
|
+
["git", "commit", "-qm", "fix: address remote verification"],
|
|
317
|
+
cwd=root,
|
|
318
|
+
check=True,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
state = begin_implementation(
|
|
322
|
+
root,
|
|
323
|
+
"change-1",
|
|
324
|
+
actor_id="worker",
|
|
325
|
+
context_id="worker-context",
|
|
326
|
+
kind="agent",
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
self.assertEqual("implementing", state["phase"])
|
|
330
|
+
self.assertEqual(2, state["cycle"])
|
|
331
|
+
self.assertEqual([], state["verification"])
|
|
332
|
+
invalidated = [
|
|
333
|
+
event
|
|
334
|
+
for event in state["history"]
|
|
335
|
+
if event["event"] == "verification-invalidated"
|
|
336
|
+
]
|
|
337
|
+
self.assertEqual(1, len(invalidated))
|
|
338
|
+
self.assertEqual(1, invalidated[0]["previousCycle"])
|
|
339
|
+
self.assertEqual(
|
|
340
|
+
"source-changed-after-verification", invalidated[0]["reason"]
|
|
341
|
+
)
|
|
342
|
+
self.assertEqual(2, len(invalidated[0]["previousVerification"]))
|
|
343
|
+
|
|
344
|
+
def test_current_verified_source_cannot_bypass_independent_review(self):
|
|
345
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
346
|
+
base = Path(directory)
|
|
347
|
+
root = base / "project"
|
|
348
|
+
inputs = base / "inputs"
|
|
349
|
+
root.mkdir()
|
|
350
|
+
inputs.mkdir()
|
|
351
|
+
self.initialize_repository(root)
|
|
352
|
+
self.prepare_verified_change(root, inputs)
|
|
353
|
+
|
|
354
|
+
with self.assertRaisesRegex(
|
|
355
|
+
ContractError, "must enter independent review"
|
|
356
|
+
):
|
|
357
|
+
begin_implementation(
|
|
358
|
+
root,
|
|
359
|
+
"change-1",
|
|
360
|
+
actor_id="worker",
|
|
361
|
+
context_id="worker-context",
|
|
362
|
+
kind="agent",
|
|
363
|
+
)
|
|
364
|
+
|
|
303
365
|
def test_full_lifecycle_requires_independent_review(self):
|
|
304
366
|
with tempfile.TemporaryDirectory() as directory:
|
|
305
367
|
base = Path(directory)
|