engineering-process 2.0.2__tar.gz → 2.2.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 (94) hide show
  1. {engineering_process-2.0.2 → engineering_process-2.2.0}/MANIFEST.in +2 -0
  2. {engineering_process-2.0.2/engineering_process.egg-info → engineering_process-2.2.0}/PKG-INFO +73 -7
  3. {engineering_process-2.0.2 → engineering_process-2.2.0}/README.md +72 -6
  4. engineering_process-2.2.0/RELEASE_NOTES.md +18 -0
  5. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/__init__.py +1 -1
  6. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/adoption.py +10 -3
  7. engineering_process-2.2.0/engineering_process/artifact_standards.py +147 -0
  8. engineering_process-2.2.0/engineering_process/automation_name.py +31 -0
  9. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/cli.py +65 -1
  10. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/contracts.py +6 -0
  11. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/distribution.py +5 -2
  12. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/lifecycle.py +16 -1
  13. engineering_process-2.0.2/engineering_process/publication_compat.py → engineering_process-2.2.0/engineering_process/pr_description.py +99 -138
  14. engineering_process-2.2.0/engineering_process/publication_compat.py +119 -0
  15. engineering_process-2.2.0/engineering_process/release_notes.py +76 -0
  16. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/repository.py +6 -0
  17. {engineering_process-2.0.2 → engineering_process-2.2.0/engineering_process.egg-info}/PKG-INFO +73 -7
  18. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/SOURCES.txt +16 -0
  19. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-complete/SKILL.md +19 -1
  20. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-implement/SKILL.md +7 -0
  21. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-plan/SKILL.md +8 -0
  22. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-review/SKILL.md +43 -2
  23. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-start/SKILL.md +15 -0
  24. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-verify/SKILL.md +7 -0
  25. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/deliver-change/SKILL.md +8 -7
  26. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/production-engineering/SKILL.md +36 -2
  27. engineering_process-2.2.0/process_assets/standards/automation-name.v1.json +16 -0
  28. engineering_process-2.2.0/process_assets/standards/pull-request.v1.json +129 -0
  29. engineering_process-2.2.0/process_assets/standards/release-notes.v1.json +34 -0
  30. {engineering_process-2.0.2 → engineering_process-2.2.0}/pyproject.toml +12 -1
  31. engineering_process-2.2.0/schemas/artifact-selection.schema.json +67 -0
  32. engineering_process-2.2.0/schemas/artifact-standard.schema.json +322 -0
  33. engineering_process-2.2.0/schemas/automation-name-data.schema.json +35 -0
  34. engineering_process-2.2.0/schemas/pr-description-data.schema.json +63 -0
  35. engineering_process-2.2.0/schemas/release-notes-data.schema.json +103 -0
  36. engineering_process-2.2.0/schemas/renovate-preset.schema.json +30 -0
  37. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/run.schema.json +1 -0
  38. engineering_process-2.2.0/templates/PULL_REQUEST_TEMPLATE.md +36 -0
  39. {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/renovate.json +1 -1
  40. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_adoption.py +60 -0
  41. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_architecture.py +5 -1
  42. engineering_process-2.2.0/tests/test_artifact_standards.py +259 -0
  43. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_automation.py +16 -1
  44. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_cli.py +6 -0
  45. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_contracts.py +11 -0
  46. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_distribution.py +13 -1
  47. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_lifecycle.py +105 -0
  48. engineering_process-2.2.0/tests/test_publication_workflow.py +92 -0
  49. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_release.py +88 -0
  50. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_repository.py +12 -1
  51. engineering_process-2.0.2/templates/PULL_REQUEST_TEMPLATE.md +0 -39
  52. {engineering_process-2.0.2 → engineering_process-2.2.0}/LICENSE +0 -0
  53. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/__main__.py +0 -0
  54. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_contract.py +0 -0
  55. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_posix.py +0 -0
  56. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_windows.py +0 -0
  57. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_windows_job.py +0 -0
  58. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/commands.py +0 -0
  59. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/helper_launch.py +0 -0
  60. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/production_engineering.py +0 -0
  61. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/project.py +0 -0
  62. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/release.py +0 -0
  63. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/requirements-dev.txt +0 -0
  64. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/requirements-runtime.txt +0 -0
  65. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/skills.py +0 -0
  66. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/supervision.py +0 -0
  67. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/dependency_links.txt +0 -0
  68. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/entry_points.txt +0 -0
  69. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/requires.txt +0 -0
  70. {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/top_level.txt +0 -0
  71. {engineering_process-2.0.2 → engineering_process-2.2.0}/process-graph.json +0 -0
  72. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/process-improve/SKILL.md +0 -0
  73. {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/production-engineering/invariants.json +0 -0
  74. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/change.schema.json +0 -0
  75. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/plan.schema.json +0 -0
  76. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/process-graph.schema.json +0 -0
  77. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/process-lock.schema.json +0 -0
  78. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/production-engineering.schema.json +0 -0
  79. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/project-legacy.schema.json +0 -0
  80. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/project.schema.json +0 -0
  81. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/receipt.schema.json +0 -0
  82. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/release-change.schema.json +0 -0
  83. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/release.schema.json +0 -0
  84. {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/review.schema.json +0 -0
  85. {engineering_process-2.0.2 → engineering_process-2.2.0}/setup.cfg +0 -0
  86. {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/AGENTS.process.md +0 -0
  87. {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/adopt-process-windows-job.py +0 -0
  88. {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/adopt-process.py +0 -0
  89. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_commands.py +0 -0
  90. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_production_engineering.py +0 -0
  91. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_publication_compat.py +0 -0
  92. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_pypi_cache_horizon.py +0 -0
  93. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_skills.py +0 -0
  94. {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_supervisor_posix.py +0 -0
@@ -1,4 +1,6 @@
1
1
  include process-graph.json
2
+ include RELEASE_NOTES.md
2
3
  recursive-include process_assets/skills SKILL.md *.json
4
+ recursive-include process_assets/standards *.json
3
5
  recursive-include schemas *.json
4
6
  recursive-include templates *.md *.py *.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-process
3
- Version: 2.0.2
3
+ Version: 2.2.0
4
4
  Summary: A small agent-neutral engineering lifecycle with managed adoption
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/phuongnse/engineering-process
@@ -69,6 +69,14 @@ may have implemented the current cycle. There is no attestation hierarchy,
69
69
  recommendation chain, authority-transition protocol, remote-evidence federation, or
70
70
  second handwritten validator.
71
71
 
72
+ The coordinator hands review to an actual reviewer. A new agent review starts in a
73
+ fresh context with the accepted source artifacts and no suggested verdict; the same
74
+ reviewer continues corrections. The reviewer inspects the change and authors its
75
+ own report. The runner's existing task/session interaction and returned result make
76
+ that work inspectable. Sharing a model, provider, or account is allowed; this is a
77
+ workflow for independent judgment, not authenticated identity or merge enforcement.
78
+ See [change-review](process_assets/skills/change-review/SKILL.md) for the handoff.
79
+
72
80
  Runtime architecture is enforced by semantic fitness functions, not module or source-
73
81
  line quotas. Every module has an explicit dependency layer, imports point toward lower
74
82
  layers, the internal graph remains acyclic, and lifecycle.py alone owns state
@@ -174,6 +182,25 @@ runtime/license delivery, Linux advisory resolution, real-host workspace securit
174
182
  updater, incident recovery, and independent security review remain planned.
175
183
  Consumers without readiness remain compatible during that evidence-backed rollout.
176
184
 
185
+ ### Design quality
186
+
187
+ Changes that materially alter logic, state, or collaboration boundaries carry
188
+ consumer design standards in their existing acceptance criteria. The shared
189
+ [design quality guidance](process_assets/skills/production-engineering/SKILL.md#design-quality)
190
+ requires understandable responsibilities, data and state, ownership, and contracts.
191
+ Agents actively introduce or refine cohesive abstractions when current requirements
192
+ justify them, weighing comprehension and change locality against indirection.
193
+ Consumer architecture and language choices remain authoritative.
194
+
195
+ Start defines scoped design outcomes; the plan explains material choices in its
196
+ existing approach and work items; implementation revisits the affected flow before
197
+ verification. Independent review traces behavior and a concrete maintenance scenario
198
+ against the actual code. Demonstrated violations of accepted criteria can block
199
+ completion despite passing tests. Routine edits stay proportional, and a clear
200
+ direct implementation remains valid. The existing lifecycle enforces criterion-bound
201
+ findings and evidence freshness; contextual design quality remains the reviewer's
202
+ judgment, without another gate, artifact, or canonical invariant.
203
+
177
204
  ### Production engineering invariants
178
205
 
179
206
  Every new plan and independent review applies one small, versioned invariant floor:
@@ -243,6 +270,11 @@ The transaction writes only managed surfaces:
243
270
 
244
271
  It removes obsolete skills named by the previous process lock and preserves
245
272
  consumer-owned skills and instructions. Applying the same version twice is a no-op.
273
+ Consumer-owned `.process/standards.json` selections and override definitions are also
274
+ preserved; the managed PR template follows the effective supported standard. See
275
+ [consumer document standards](ARTIFACT_STANDARDS.md).
276
+ [Automation naming](ARTIFACT_STANDARDS.md#automation-names) uses the same versioned
277
+ selection and override mechanism; consumers apply it in their bootstrap and provider checks.
246
278
  The legacy managed runner can enter 1.0 directly, so consumers do not need a chain of
247
279
  per-version migration documents. The same transaction deletes the retired migration
248
280
  directory and standing automation policy; the Windows Job Object helper remains a
@@ -440,9 +472,22 @@ approved can finish only while the repository still matches the reviewed snapsho
440
472
  --actor coordinator \
441
473
  --context finish-123
442
474
 
475
+ New runs preserve the accepted `comparisonBase` ref and contract digest, and record
476
+ its resolved commit separately as `comparisonBaseCommit`. Start rejects missing or
477
+ non-commit refs before writing the run. Lifecycle output supplies the recorded commit
478
+ for review even after commits or branch movement. Older runs without this field stay
479
+ readable; their original review boundary must be established from available history,
480
+ not retrospectively claimed as pinned. If that boundary cannot be established, use
481
+ an owner-selected replacement contract.
482
+
483
+ Correction reports must retain every previously open blocker until its unchanged
484
+ identity receives an explicit `resolved` disposition. Omitting it or changing it to
485
+ `accepted-risk` or `tracked-follow-up` cannot retire the blocker. This applies to all
486
+ supported review schemas; ordinary legacy non-blocking observations remain readable.
487
+
443
488
  ### Public pull-request evidence
444
489
 
445
- The managed pull-request template keeps public assurance separate from local
490
+ The default pull-request standard keeps public assurance separate from local
446
491
  lifecycle identity. Its five sections and labeled fields are ordered and stable:
447
492
  outcome and scope; source, risk, compatibility, and stack; profiles, snapshot, and
448
493
  completion receipt; verdict, cycles, blocking status, and non-blocking dispositions;
@@ -452,10 +497,15 @@ handle, or local `.process/runs` path. Those values remain in lifecycle state, w
452
497
  they enforce self-review rejection but do not pretend to be provider-authenticated
453
498
  review identities.
454
499
 
455
- `processctl publication validate-pr` checks that public contract deterministically.
500
+ The template and validator derive this contract from the same versioned definition.
501
+ Consumers can select a supported override through `.process/standards.json`; see
502
+ [generation, verification and custom-format boundaries](ARTIFACT_STANDARDS.md).
503
+ `processctl publication validate-pr` checks that selected contract deterministically.
456
504
  It rejects missing, repeated, misplaced, hidden, unordered, or unsupported visible
457
505
  structure. Completion checkboxes belong only to the Completion gate section. Ready
458
- pull requests must have every checkbox checked; drafts may retain unchecked work.
506
+ pull requests must have every checkbox checked and no unresolved default placeholder
507
+ values; drafts may retain pending fields and unchecked work. The author/coordinator
508
+ replaces them with actual evidence before ready/merge; the reviewer supplies the verdict.
459
509
  One trailing `Refs ISSUE.` line remains optional. A ready, contract-identified final
460
510
  consumer adoption may instead use `Closes ISSUE, closes OWNER/REPOSITORY#NUMBER.` with
461
511
  the complete keyword/reference syntax repeated for every issue; drafts cannot close
@@ -465,12 +515,26 @@ and authors plus independent review keep it out of free-form values. The validat
465
515
  a positive grammar for public fields; it deliberately does not guess identities from
466
516
  an open-ended vocabulary of names or labels.
467
517
 
518
+ This producer's local `review` profile runs `verification/verify_publication.py`
519
+ against the actual Git branch. `main` is the consumer's integration branch, not a
520
+ proposal; detached local checkouts require explicit PR context instead of a guessed
521
+ branch name. In CI, the `Adopted public process` job supplies actual PR metadata and
522
+ the base/head commit range to the installed publication adapters. Metadata edits and
523
+ draft-state changes rerun CI. Maintainers must keep this existing job in `main`'s
524
+ required status checks alongside the other required checks. These publication choices
525
+ belong to this consumer; the shared lifecycle does not impose a naming policy.
526
+
468
527
  At any point:
469
528
 
470
529
  processctl change status --change-id change-123 --json
471
530
 
472
531
  ## Release to consumer PR
473
532
 
533
+ Each release includes [reviewed release contents](RELEASE_NOTES.md) generated from
534
+ the canonical manifest: shipped features/fixes, their source issues or changes, and
535
+ upgrade guidance. The release PR reviews this file; the GitHub Release publishes the
536
+ same contents. See [the release procedure](RELEASING.md) for authoring and checks.
537
+
474
538
  This producer's release identity inputs and text assets declared by
475
539
  `tool.setuptools.data-files` use UTF-8 without BOM and LF, matching `.gitattributes`.
476
540
  Writers select that representation explicitly; JSON writers use
@@ -501,17 +565,19 @@ authorizes that merge.
501
565
  This repository opts in through .github/renovate.json, so it receives the same
502
566
  adoption PR as every other consumer. See SELF_HOSTING.md and RELEASING.md.
503
567
 
504
- The optional [Renovate preset](templates/renovate.json) is generated from the
505
- canonical public template. It supplies only `prHeader` and `prBodyTemplate`;
568
+ The optional [Renovate preset](templates/renovate.json) and public template are generated
569
+ from the packaged PR standard. It supplies only `prHeader` and `prBodyTemplate`;
506
570
  dependency selection, supported platforms, schedules, major-update approval,
507
571
  commands, draft policy, and merge authority remain consumer-owned. Regenerate it
508
572
  with `python verification/generate_renovate_preset.py`; `--check` rejects drift.
573
+ Consumers overriding that standard can generate a matching preset with
574
+ `processctl artifact renovate-preset`; wire it through their own Renovate configuration.
509
575
 
510
576
  Consumers add `github>phuongnse/engineering-process//templates/renovate#COMMIT_SHA`
511
577
  to their existing `extends` array, replacing `COMMIT_SHA` with the full source
512
578
  commit of a verified release that contains the preset. Remove obsolete inline
513
579
  `prHeader` and `prBodyTemplate` overrides, including matching package-rule overrides.
514
- The preset targets the canonical draft grammar used by 1.2.4 and this distribution;
580
+ The default preset targets the draft grammar used by 1.2.4 and this distribution;
515
581
  it does not claim compatibility with earlier publication contracts. A future
516
582
  grammar change must preserve this adapter or ship an explicit consumer migration.
517
583
 
@@ -45,6 +45,14 @@ may have implemented the current cycle. There is no attestation hierarchy,
45
45
  recommendation chain, authority-transition protocol, remote-evidence federation, or
46
46
  second handwritten validator.
47
47
 
48
+ The coordinator hands review to an actual reviewer. A new agent review starts in a
49
+ fresh context with the accepted source artifacts and no suggested verdict; the same
50
+ reviewer continues corrections. The reviewer inspects the change and authors its
51
+ own report. The runner's existing task/session interaction and returned result make
52
+ that work inspectable. Sharing a model, provider, or account is allowed; this is a
53
+ workflow for independent judgment, not authenticated identity or merge enforcement.
54
+ See [change-review](process_assets/skills/change-review/SKILL.md) for the handoff.
55
+
48
56
  Runtime architecture is enforced by semantic fitness functions, not module or source-
49
57
  line quotas. Every module has an explicit dependency layer, imports point toward lower
50
58
  layers, the internal graph remains acyclic, and lifecycle.py alone owns state
@@ -150,6 +158,25 @@ runtime/license delivery, Linux advisory resolution, real-host workspace securit
150
158
  updater, incident recovery, and independent security review remain planned.
151
159
  Consumers without readiness remain compatible during that evidence-backed rollout.
152
160
 
161
+ ### Design quality
162
+
163
+ Changes that materially alter logic, state, or collaboration boundaries carry
164
+ consumer design standards in their existing acceptance criteria. The shared
165
+ [design quality guidance](process_assets/skills/production-engineering/SKILL.md#design-quality)
166
+ requires understandable responsibilities, data and state, ownership, and contracts.
167
+ Agents actively introduce or refine cohesive abstractions when current requirements
168
+ justify them, weighing comprehension and change locality against indirection.
169
+ Consumer architecture and language choices remain authoritative.
170
+
171
+ Start defines scoped design outcomes; the plan explains material choices in its
172
+ existing approach and work items; implementation revisits the affected flow before
173
+ verification. Independent review traces behavior and a concrete maintenance scenario
174
+ against the actual code. Demonstrated violations of accepted criteria can block
175
+ completion despite passing tests. Routine edits stay proportional, and a clear
176
+ direct implementation remains valid. The existing lifecycle enforces criterion-bound
177
+ findings and evidence freshness; contextual design quality remains the reviewer's
178
+ judgment, without another gate, artifact, or canonical invariant.
179
+
153
180
  ### Production engineering invariants
154
181
 
155
182
  Every new plan and independent review applies one small, versioned invariant floor:
@@ -219,6 +246,11 @@ The transaction writes only managed surfaces:
219
246
 
220
247
  It removes obsolete skills named by the previous process lock and preserves
221
248
  consumer-owned skills and instructions. Applying the same version twice is a no-op.
249
+ Consumer-owned `.process/standards.json` selections and override definitions are also
250
+ preserved; the managed PR template follows the effective supported standard. See
251
+ [consumer document standards](ARTIFACT_STANDARDS.md).
252
+ [Automation naming](ARTIFACT_STANDARDS.md#automation-names) uses the same versioned
253
+ selection and override mechanism; consumers apply it in their bootstrap and provider checks.
222
254
  The legacy managed runner can enter 1.0 directly, so consumers do not need a chain of
223
255
  per-version migration documents. The same transaction deletes the retired migration
224
256
  directory and standing automation policy; the Windows Job Object helper remains a
@@ -416,9 +448,22 @@ approved can finish only while the repository still matches the reviewed snapsho
416
448
  --actor coordinator \
417
449
  --context finish-123
418
450
 
451
+ New runs preserve the accepted `comparisonBase` ref and contract digest, and record
452
+ its resolved commit separately as `comparisonBaseCommit`. Start rejects missing or
453
+ non-commit refs before writing the run. Lifecycle output supplies the recorded commit
454
+ for review even after commits or branch movement. Older runs without this field stay
455
+ readable; their original review boundary must be established from available history,
456
+ not retrospectively claimed as pinned. If that boundary cannot be established, use
457
+ an owner-selected replacement contract.
458
+
459
+ Correction reports must retain every previously open blocker until its unchanged
460
+ identity receives an explicit `resolved` disposition. Omitting it or changing it to
461
+ `accepted-risk` or `tracked-follow-up` cannot retire the blocker. This applies to all
462
+ supported review schemas; ordinary legacy non-blocking observations remain readable.
463
+
419
464
  ### Public pull-request evidence
420
465
 
421
- The managed pull-request template keeps public assurance separate from local
466
+ The default pull-request standard keeps public assurance separate from local
422
467
  lifecycle identity. Its five sections and labeled fields are ordered and stable:
423
468
  outcome and scope; source, risk, compatibility, and stack; profiles, snapshot, and
424
469
  completion receipt; verdict, cycles, blocking status, and non-blocking dispositions;
@@ -428,10 +473,15 @@ handle, or local `.process/runs` path. Those values remain in lifecycle state, w
428
473
  they enforce self-review rejection but do not pretend to be provider-authenticated
429
474
  review identities.
430
475
 
431
- `processctl publication validate-pr` checks that public contract deterministically.
476
+ The template and validator derive this contract from the same versioned definition.
477
+ Consumers can select a supported override through `.process/standards.json`; see
478
+ [generation, verification and custom-format boundaries](ARTIFACT_STANDARDS.md).
479
+ `processctl publication validate-pr` checks that selected contract deterministically.
432
480
  It rejects missing, repeated, misplaced, hidden, unordered, or unsupported visible
433
481
  structure. Completion checkboxes belong only to the Completion gate section. Ready
434
- pull requests must have every checkbox checked; drafts may retain unchecked work.
482
+ pull requests must have every checkbox checked and no unresolved default placeholder
483
+ values; drafts may retain pending fields and unchecked work. The author/coordinator
484
+ replaces them with actual evidence before ready/merge; the reviewer supplies the verdict.
435
485
  One trailing `Refs ISSUE.` line remains optional. A ready, contract-identified final
436
486
  consumer adoption may instead use `Closes ISSUE, closes OWNER/REPOSITORY#NUMBER.` with
437
487
  the complete keyword/reference syntax repeated for every issue; drafts cannot close
@@ -441,12 +491,26 @@ and authors plus independent review keep it out of free-form values. The validat
441
491
  a positive grammar for public fields; it deliberately does not guess identities from
442
492
  an open-ended vocabulary of names or labels.
443
493
 
494
+ This producer's local `review` profile runs `verification/verify_publication.py`
495
+ against the actual Git branch. `main` is the consumer's integration branch, not a
496
+ proposal; detached local checkouts require explicit PR context instead of a guessed
497
+ branch name. In CI, the `Adopted public process` job supplies actual PR metadata and
498
+ the base/head commit range to the installed publication adapters. Metadata edits and
499
+ draft-state changes rerun CI. Maintainers must keep this existing job in `main`'s
500
+ required status checks alongside the other required checks. These publication choices
501
+ belong to this consumer; the shared lifecycle does not impose a naming policy.
502
+
444
503
  At any point:
445
504
 
446
505
  processctl change status --change-id change-123 --json
447
506
 
448
507
  ## Release to consumer PR
449
508
 
509
+ Each release includes [reviewed release contents](RELEASE_NOTES.md) generated from
510
+ the canonical manifest: shipped features/fixes, their source issues or changes, and
511
+ upgrade guidance. The release PR reviews this file; the GitHub Release publishes the
512
+ same contents. See [the release procedure](RELEASING.md) for authoring and checks.
513
+
450
514
  This producer's release identity inputs and text assets declared by
451
515
  `tool.setuptools.data-files` use UTF-8 without BOM and LF, matching `.gitattributes`.
452
516
  Writers select that representation explicitly; JSON writers use
@@ -477,17 +541,19 @@ authorizes that merge.
477
541
  This repository opts in through .github/renovate.json, so it receives the same
478
542
  adoption PR as every other consumer. See SELF_HOSTING.md and RELEASING.md.
479
543
 
480
- The optional [Renovate preset](templates/renovate.json) is generated from the
481
- canonical public template. It supplies only `prHeader` and `prBodyTemplate`;
544
+ The optional [Renovate preset](templates/renovate.json) and public template are generated
545
+ from the packaged PR standard. It supplies only `prHeader` and `prBodyTemplate`;
482
546
  dependency selection, supported platforms, schedules, major-update approval,
483
547
  commands, draft policy, and merge authority remain consumer-owned. Regenerate it
484
548
  with `python verification/generate_renovate_preset.py`; `--check` rejects drift.
549
+ Consumers overriding that standard can generate a matching preset with
550
+ `processctl artifact renovate-preset`; wire it through their own Renovate configuration.
485
551
 
486
552
  Consumers add `github>phuongnse/engineering-process//templates/renovate#COMMIT_SHA`
487
553
  to their existing `extends` array, replacing `COMMIT_SHA` with the full source
488
554
  commit of a verified release that contains the preset. Remove obsolete inline
489
555
  `prHeader` and `prBodyTemplate` overrides, including matching package-rule overrides.
490
- The preset targets the canonical draft grammar used by 1.2.4 and this distribution;
556
+ The default preset targets the draft grammar used by 1.2.4 and this distribution;
491
557
  it does not claim compatibility with earlier publication contracts. A future
492
558
  grammar change must preserve this adapter or ship an explicit consumer migration.
493
559
 
@@ -0,0 +1,18 @@
1
+ # Engineering Process v2.2.0
2
+
3
+ Changes since v2.1.0.
4
+
5
+ ## Features
6
+
7
+ - Generate and verify PR descriptions and release notes from versioned standards with consumer\-owned overrides preserved by adoption\. Matching template and validator definitions reject unresolved default PR placeholders in ready state\; custom formats retain consumer\-owned validation profiles\. ([#174](https://github.com/phuongnse/engineering-process/issues/174))
8
+ - Generate and verify automation names from a default owner\-role convention or a consumer\-owned override\. Bootstrap integrations can consume structured CLI output and check actual provider names before side effects\; provider limits and authenticated identity remain consumer\-owned\. ([#176](https://github.com/phuongnse/engineering-process/issues/176))
9
+
10
+ ## Upgrade and compatibility
11
+
12
+ Merge the complete hash-locked package/adoption PR, update the local and CI environments to the selected version, and start a fresh agent session.
13
+
14
+ Consumer CI, naming conventions and branch-protection settings remain consumer-owned; adoption does not configure them automatically.
15
+
16
+ See [versioning and compatibility](https://github.com/phuongnse/engineering-process/blob/v2.2.0/VERSIONING.md) and [adoption guidance](https://github.com/phuongnse/engineering-process/blob/v2.2.0/SELF_HOSTING.md).
17
+
18
+ [Full change comparison](https://github.com/phuongnse/engineering-process/compare/v2.1.0...v2.2.0)
@@ -1,3 +1,3 @@
1
1
  """Agent-neutral engineering process."""
2
2
 
3
- VERSION = "2.0.2"
3
+ VERSION = "2.2.0"
@@ -12,6 +12,7 @@ import tempfile
12
12
  from typing import Any, Callable
13
13
 
14
14
  from . import VERSION
15
+ from .artifact_standards import load_standard_catalog
15
16
  from .contracts import ProcessError, read_json, validate_document
16
17
  from .distribution import (
17
18
  distribution_digest,
@@ -21,6 +22,7 @@ from .distribution import (
21
22
  skills_root,
22
23
  )
23
24
  from .project import normalize_project, project_path
25
+ from .pr_description import render_template
24
26
 
25
27
 
26
28
  MAX_REQUIREMENTS_BYTES = 2_000_000
@@ -253,9 +255,8 @@ def _expected_files(
253
255
  "utf-8"
254
256
  )
255
257
 
256
- pull_request_template = (
257
- process_root / "templates" / "PULL_REQUEST_TEMPLATE.md"
258
- ).read_text(encoding="utf-8")
258
+ standards = load_standard_catalog(project_root, process_root)
259
+ pull_request_template = render_template(standards.resolve("pull-request"), process_root=process_root)
259
260
  pull_request_path = project_root / ".github" / "PULL_REQUEST_TEMPLATE.md"
260
261
  existing_pull_request = (
261
262
  pull_request_path.read_text(encoding="utf-8")
@@ -301,6 +302,12 @@ def _expected_files(
301
302
  writes[Path(".process/process.lock")] = (
302
303
  json.dumps(lock, indent=2, sort_keys=True) + "\n"
303
304
  ).encode("utf-8")
305
+ collisions = standards.consumer_files.intersection(writes.keys() | deletions)
306
+ if collisions:
307
+ raise ProcessError(
308
+ "consumer standard files conflict with managed adoption paths; move the definitions and update the selection: "
309
+ + ", ".join(sorted(path.as_posix() for path in collisions))
310
+ )
304
311
  total = sum(len(value) for value in writes.values())
305
312
  if total > MAX_MANAGED_BYTES:
306
313
  raise ProcessError("managed adoption output exceeds its aggregate limit")
@@ -0,0 +1,147 @@
1
+ """Resolve one versioned artifact contract for both generation and verification."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ import os
7
+ from pathlib import Path
8
+ import re
9
+ from typing import Any
10
+
11
+ from .contracts import ProcessError, digest_json, load_and_validate
12
+ from .distribution import schemas_root
13
+ from .repository import STATE_PREFIXES, _git
14
+
15
+
16
+ IDENTIFIER = re.compile(r"[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?")
17
+ MAX_DOCUMENT_BYTES = 1_000_000
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class ArtifactStandard:
22
+ document: dict[str, Any]
23
+ source: str
24
+
25
+ @property
26
+ def rules(self) -> dict[str, Any]:
27
+ return self.document["rules"]
28
+
29
+ @property
30
+ def metadata(self) -> dict[str, Any]:
31
+ return {
32
+ **{key: self.document[key] for key in ("id", "version", "artifact", "adapter")},
33
+ "digest": digest_json(self.document),
34
+ "source": self.source,
35
+ }
36
+
37
+ def unresolved(self, value: str) -> bool:
38
+ # These are reserved values in an owned protocol, not prose classification.
39
+ return value.strip().casefold() in {
40
+ item.strip().casefold() for item in self.document.get("pendingValues", [])
41
+ }
42
+
43
+
44
+ def _consumer_file(root: Path, relative: str) -> Path:
45
+ path = root / relative
46
+ if not path.resolve().is_relative_to(root):
47
+ raise ProcessError(f"standard path escapes the consumer repository: {relative}")
48
+ if path.resolve() != path or Path(relative).as_posix() != relative:
49
+ raise ProcessError(f"standard path must be canonical without links: {relative}")
50
+ if any(part in {"..", ".git"} for part in Path(relative).parts):
51
+ raise ProcessError(f"standard path is not an owned repository file: {relative}")
52
+ current = root
53
+ for part in Path(relative).parts:
54
+ current /= part
55
+ if current.is_symlink():
56
+ raise ProcessError(f"standard paths cannot traverse symlinks: {relative}")
57
+ if not path.is_file():
58
+ raise ProcessError(f"consumer standard file is missing: {relative}")
59
+ encoded = os.fsencode(Path(relative).as_posix())
60
+ if any(encoded.startswith(prefix) for prefix in STATE_PREFIXES):
61
+ raise ProcessError(f"standard file is excluded from lifecycle snapshots: {relative}")
62
+ inventory = _git(root, ["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", relative])
63
+ if encoded not in inventory.split(b"\0"):
64
+ raise ProcessError(f"standard file must be included in the consumer Git snapshot: {relative}")
65
+ return path
66
+
67
+
68
+ def _unique(values: list[str], description: str) -> None:
69
+ if len(values) != len(set(values)):
70
+ raise ProcessError(f"artifact standard requires unique {description}")
71
+
72
+
73
+ def _validate_relations(document: dict[str, Any]) -> None:
74
+ _unique([value.strip().casefold() for value in document.get("pendingValues", [])], "pending values")
75
+ rules = document["rules"]
76
+ if document["adapter"] == "pr-description":
77
+ sections = rules["sections"]
78
+ _unique([section["heading"] for section in sections], "section headings")
79
+ for kind in ("fields", "checks"):
80
+ entries = [entry for section in sections for entry in section[kind]]
81
+ _unique([entry["id"] for entry in entries], f"{kind} ids")
82
+ _unique([entry["label"] for entry in entries], f"{kind} labels")
83
+ elif document["adapter"] == "release-notes":
84
+ _unique([group["type"] for group in rules["groups"]], "change types")
85
+ _unique([section["id"] for section in rules["sections"]], "release section ids")
86
+ _unique([item["heading"] for item in rules["groups"] + rules["sections"]], "release headings")
87
+
88
+
89
+ def read_document(path: Path) -> bytes:
90
+ with path.open("rb") as stream:
91
+ data = stream.read(MAX_DOCUMENT_BYTES + 1)
92
+ if len(data) > MAX_DOCUMENT_BYTES:
93
+ raise ProcessError("artifact body exceeds its size limit")
94
+ data.decode("utf-8")
95
+ return data
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class StandardCatalog:
100
+ """One selection snapshot for adapter resolution and adoption ownership checks."""
101
+
102
+ project_root: Path | None
103
+ process_root: Path
104
+ selections: dict[str, Any]
105
+ consumer_files: frozenset[Path]
106
+
107
+ def resolve(self, artifact: str) -> ArtifactStandard:
108
+ if len(artifact) > 128 or not IDENTIFIER.fullmatch(artifact):
109
+ raise ProcessError("artifact must be a canonical identifier")
110
+ selection = self.selections.get(artifact, {"builtin": f"{artifact}@1"})
111
+ if "path" in selection:
112
+ path = _consumer_file(self.project_root, selection["path"])
113
+ source = selection["path"]
114
+ else:
115
+ name, version = selection["builtin"].rsplit("@", 1)
116
+ path = self.process_root / "process_assets" / "standards" / f"{name}.v{version}.json"
117
+ source = selection["builtin"]
118
+ if path.is_symlink() or not path.is_file():
119
+ raise ProcessError(f"unsupported packaged artifact standard: {source}")
120
+ document = load_and_validate(path, "artifact-standard", schema_root=schemas_root(self.process_root))
121
+ if document["artifact"] != artifact:
122
+ raise ProcessError(f"selected standard is for {document['artifact']}, not {artifact}")
123
+ if "builtin" in selection and (name != artifact or document["version"] != int(version)):
124
+ raise ProcessError("packaged standard identity does not match its selection")
125
+ _validate_relations(document)
126
+ return ArtifactStandard(document, source)
127
+
128
+
129
+ def load_standard_catalog(project_root: Path | None, process_root: Path) -> StandardCatalog:
130
+ selections: dict[str, Any] = {}
131
+ consumer_files: set[Path] = set()
132
+ if project_root is not None:
133
+ project_root = project_root.resolve()
134
+ selector = project_root / ".process" / "standards.json"
135
+ if selector.exists() or selector.is_symlink():
136
+ document = load_and_validate(
137
+ _consumer_file(project_root, ".process/standards.json"),
138
+ "artifact-selection", schema_root=schemas_root(process_root),
139
+ )
140
+ selections = document["artifacts"]
141
+ consumer_files = {Path(".process/standards.json")}
142
+ consumer_files.update(Path(value["path"]) for value in selections.values() if "path" in value)
143
+ return StandardCatalog(project_root, process_root, selections, frozenset(consumer_files))
144
+
145
+
146
+ def resolve_standard(project_root: Path | None, process_root: Path, artifact: str) -> ArtifactStandard:
147
+ return load_standard_catalog(project_root, process_root).resolve(artifact)
@@ -0,0 +1,31 @@
1
+ """Compose automation names from a selected convention and consumer-owned components."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from .artifact_standards import ArtifactStandard
7
+ from .contracts import ProcessError, validate_document
8
+ from .distribution import distribution_root, schemas_root
9
+
10
+
11
+ def render_name(
12
+ standard: ArtifactStandard,
13
+ data: dict[str, Any],
14
+ *,
15
+ state: str = "ready",
16
+ process_root: Path | None = None,
17
+ ) -> str:
18
+ if standard.document["adapter"] != "automation-name":
19
+ raise ProcessError("selected standard requires a different name adapter")
20
+ validate_document(data, "automation-name-data", schema_root=schemas_root(distribution_root(process_root)))
21
+ if state not in {"draft", "ready"}:
22
+ raise ProcessError("artifact state must be draft or ready")
23
+ rules = standard.rules
24
+ if set(data["components"]) != set(rules["components"]):
25
+ raise ProcessError("name components must exactly match the selected standard")
26
+ name = rules["separator"].join(data["components"][key] for key in rules["components"])
27
+ if rules["case"] == "lower":
28
+ name = name.lower()
29
+ if len(name) > rules["maxLength"]:
30
+ raise ProcessError("automation name exceeds the selected length limit")
31
+ return name + "\n"