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.
Files changed (133) hide show
  1. {engineering_process-0.2.1/engineering_process.egg-info → engineering_process-0.4.0}/PKG-INFO +36 -1
  2. {engineering_process-0.2.1 → engineering_process-0.4.0}/PRODUCTION_STANDARD.md +18 -0
  3. {engineering_process-0.2.1 → engineering_process-0.4.0}/README.md +35 -0
  4. {engineering_process-0.2.1 → engineering_process-0.4.0}/VERSIONING.md +14 -0
  5. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/__init__.py +1 -1
  6. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_supervisor_posix.py +15 -2
  7. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/lifecycle.py +36 -1
  8. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/supervision.py +3 -0
  9. {engineering_process-0.2.1 → engineering_process-0.4.0/engineering_process.egg-info}/PKG-INFO +36 -1
  10. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/SOURCES.txt +1 -0
  11. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/evolve-process/SKILL.md +13 -5
  12. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/review-change/SKILL.md +3 -0
  13. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-change/SKILL.md +6 -2
  14. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-change/references/execution.md +31 -0
  15. {engineering_process-0.2.1 → engineering_process-0.4.0}/pyproject.toml +1 -1
  16. engineering_process-0.4.0/release.json +55 -0
  17. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_environment.py +34 -0
  18. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_lifecycle.py +62 -0
  19. engineering_process-0.4.0/tests/test_process_install.py +235 -0
  20. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_self_hosting.py +36 -1
  21. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_skills.py +21 -0
  22. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_supervision.py +62 -1
  23. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_windows_job.py +7 -0
  24. engineering_process-0.2.1/release.json +0 -106
  25. {engineering_process-0.2.1 → engineering_process-0.4.0}/LICENSE +0 -0
  26. {engineering_process-0.2.1 → engineering_process-0.4.0}/MANIFEST.in +0 -0
  27. {engineering_process-0.2.1 → engineering_process-0.4.0}/bundles.json +0 -0
  28. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/__main__.py +0 -0
  29. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_download_worker.py +0 -0
  30. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_supervisor_windows.py +0 -0
  31. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/_windows_job.py +0 -0
  32. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/adoption.py +0 -0
  33. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/artifact_attestation.py +0 -0
  34. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/bootstrap.py +0 -0
  35. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/bundles.py +0 -0
  36. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/cli.py +0 -0
  37. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/contracts.py +0 -0
  38. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/distribution.py +0 -0
  39. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/distribution_verify.py +0 -0
  40. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/environment.py +0 -0
  41. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/evidence.py +0 -0
  42. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/git.py +0 -0
  43. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/git_attributes.py +0 -0
  44. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/helper_launch.py +0 -0
  45. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/impact.py +0 -0
  46. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/managed.py +0 -0
  47. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/markdown.py +0 -0
  48. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/publication.py +0 -0
  49. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/release.py +0 -0
  50. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/release_candidate.py +0 -0
  51. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-build.txt +0 -0
  52. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-dev.txt +0 -0
  53. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-release.txt +0 -0
  54. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/requirements-runtime.txt +0 -0
  55. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/runner.py +0 -0
  56. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/runtime.py +0 -0
  57. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/skills.py +0 -0
  58. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/supplemental.py +0 -0
  59. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/syncing.py +0 -0
  60. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process/tooling.py +0 -0
  61. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/dependency_links.txt +0 -0
  62. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/entry_points.txt +0 -0
  63. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/requires.txt +0 -0
  64. {engineering_process-0.2.1 → engineering_process-0.4.0}/engineering_process.egg-info/top_level.txt +0 -0
  65. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/adoption-migration.json +0 -0
  66. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/change.json +0 -0
  67. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/plan.json +0 -0
  68. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/project.json +0 -0
  69. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/release-change.json +0 -0
  70. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/release.json +0 -0
  71. {engineering_process-0.2.1 → engineering_process-0.4.0}/examples/review.json +0 -0
  72. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/assess-design/SKILL.md +0 -0
  73. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/build-frontend/SKILL.md +0 -0
  74. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/build-frontend-foundation/SKILL.md +0 -0
  75. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/change-api/SKILL.md +0 -0
  76. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/cross-repo-change/SKILL.md +0 -0
  77. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/define-change-contract/SKILL.md +0 -0
  78. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/design-module/SKILL.md +0 -0
  79. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/finish-change/SKILL.md +0 -0
  80. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/govern-ui/SKILL.md +0 -0
  81. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-change/SKILL.md +0 -0
  82. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-module/SKILL.md +0 -0
  83. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/implement-use-case/SKILL.md +0 -0
  84. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/integrate-mcp/SKILL.md +0 -0
  85. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/maintain-docs/SKILL.md +0 -0
  86. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/plan-change/SKILL.md +0 -0
  87. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/publish-change/SKILL.md +0 -0
  88. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/run-project-command/SKILL.md +0 -0
  89. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/specify-use-case/SKILL.md +0 -0
  90. {engineering_process-0.2.1 → engineering_process-0.4.0}/process_assets/skills/verify-change/SKILL.md +0 -0
  91. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/adoption-migration.schema.json +0 -0
  92. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/change.schema.json +0 -0
  93. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/evidence-receipt.schema.json +0 -0
  94. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/lifecycle.schema.json +0 -0
  95. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/plan.schema.json +0 -0
  96. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/process-lock.schema.json +0 -0
  97. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/project.schema.json +0 -0
  98. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/release-change.schema.json +0 -0
  99. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/release.schema.json +0 -0
  100. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/review.schema.json +0 -0
  101. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/reviewer-attestation.schema.json +0 -0
  102. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/supplemental-verification.schema.json +0 -0
  103. {engineering_process-0.2.1 → engineering_process-0.4.0}/schemas/verification.schema.json +0 -0
  104. {engineering_process-0.2.1 → engineering_process-0.4.0}/setup.cfg +0 -0
  105. {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/AGENTS.process.md +0 -0
  106. {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
  107. {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/adopt-process-windows-job.py +0 -0
  108. {engineering_process-0.2.1 → engineering_process-0.4.0}/templates/adopt-process.py +0 -0
  109. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_adoption.py +0 -0
  110. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_adoption_runner.py +0 -0
  111. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_artifact_attestation.py +0 -0
  112. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_bootstrap.py +0 -0
  113. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_bundles.py +0 -0
  114. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_cli.py +0 -0
  115. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_contracts.py +0 -0
  116. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_distribution.py +0 -0
  117. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_distribution_verify.py +0 -0
  118. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_evals.py +0 -0
  119. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_impact.py +0 -0
  120. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_publication.py +0 -0
  121. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_publish_event.py +0 -0
  122. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_pypi_publication.py +0 -0
  123. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release.py +0 -0
  124. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_candidate.py +0 -0
  125. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_preparation.py +0 -0
  126. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_release_qualification.py +0 -0
  127. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_runner.py +0 -0
  128. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_runtime.py +0 -0
  129. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_schemas.py +0 -0
  130. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_source_checkout.py +0 -0
  131. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_supplemental.py +0 -0
  132. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_syncing.py +0 -0
  133. {engineering_process-0.2.1 → engineering_process-0.4.0}/tests/test_tooling.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-process
3
- Version: 0.2.1
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
@@ -1,3 +1,3 @@
1
1
  """Agent-neutral engineering process."""
2
2
 
3
- VERSION = "0.2.1"
3
+ VERSION = "0.4.0"
@@ -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"] = []
@@ -14,6 +14,9 @@ import subprocess
14
14
  from typing import Mapping, Protocol
15
15
 
16
16
 
17
+ NATURAL_DRAIN_GRACE_MILLISECONDS = 250
18
+
19
+
17
20
  @dataclass(frozen=True)
18
21
  class CleanupOutcome:
19
22
  """Result of a bounded process-tree cleanup operation."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-process
3
- Version: 0.2.1
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
@@ -109,6 +109,7 @@ tests/test_environment.py
109
109
  tests/test_evals.py
110
110
  tests/test_impact.py
111
111
  tests/test_lifecycle.py
112
+ tests/test_process_install.py
112
113
  tests/test_publication.py
113
114
  tests/test_publish_event.py
114
115
  tests/test_pypi_publication.py
@@ -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. Classify the evidence as a project-local defect, reusable process rule,
18
- deterministic invariant, portability gap, or obsolete rule.
19
- 3. Fix local behavior in the project owner. Change shared skills only for portable
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 a regression case at the lowest reliable boundary. For skill behavior, run a
22
- realistic forward test without providing the expected answer.
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
 
@@ -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
@@ -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. After a valid finding, preserve the reviewed checkpoint, begin the next
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
- 5. Report the processctl phase, cycle, current evidence, blockers, and next owner.
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "engineering-process"
7
- version = "0.2.1"
7
+ version = "0.4.0"
8
8
  description = "Agent-neutral end-to-end engineering lifecycle and deterministic process CLI"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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)