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.
- {engineering_process-2.0.2 → engineering_process-2.2.0}/MANIFEST.in +2 -0
- {engineering_process-2.0.2/engineering_process.egg-info → engineering_process-2.2.0}/PKG-INFO +73 -7
- {engineering_process-2.0.2 → engineering_process-2.2.0}/README.md +72 -6
- engineering_process-2.2.0/RELEASE_NOTES.md +18 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/__init__.py +1 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/adoption.py +10 -3
- engineering_process-2.2.0/engineering_process/artifact_standards.py +147 -0
- engineering_process-2.2.0/engineering_process/automation_name.py +31 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/cli.py +65 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/contracts.py +6 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/distribution.py +5 -2
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/lifecycle.py +16 -1
- engineering_process-2.0.2/engineering_process/publication_compat.py → engineering_process-2.2.0/engineering_process/pr_description.py +99 -138
- engineering_process-2.2.0/engineering_process/publication_compat.py +119 -0
- engineering_process-2.2.0/engineering_process/release_notes.py +76 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/repository.py +6 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0/engineering_process.egg-info}/PKG-INFO +73 -7
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/SOURCES.txt +16 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-complete/SKILL.md +19 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-implement/SKILL.md +7 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-plan/SKILL.md +8 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-review/SKILL.md +43 -2
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-start/SKILL.md +15 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/change-verify/SKILL.md +7 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/deliver-change/SKILL.md +8 -7
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/production-engineering/SKILL.md +36 -2
- engineering_process-2.2.0/process_assets/standards/automation-name.v1.json +16 -0
- engineering_process-2.2.0/process_assets/standards/pull-request.v1.json +129 -0
- engineering_process-2.2.0/process_assets/standards/release-notes.v1.json +34 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/pyproject.toml +12 -1
- engineering_process-2.2.0/schemas/artifact-selection.schema.json +67 -0
- engineering_process-2.2.0/schemas/artifact-standard.schema.json +322 -0
- engineering_process-2.2.0/schemas/automation-name-data.schema.json +35 -0
- engineering_process-2.2.0/schemas/pr-description-data.schema.json +63 -0
- engineering_process-2.2.0/schemas/release-notes-data.schema.json +103 -0
- engineering_process-2.2.0/schemas/renovate-preset.schema.json +30 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/run.schema.json +1 -0
- engineering_process-2.2.0/templates/PULL_REQUEST_TEMPLATE.md +36 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/renovate.json +1 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_adoption.py +60 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_architecture.py +5 -1
- engineering_process-2.2.0/tests/test_artifact_standards.py +259 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_automation.py +16 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_cli.py +6 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_contracts.py +11 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_distribution.py +13 -1
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_lifecycle.py +105 -0
- engineering_process-2.2.0/tests/test_publication_workflow.py +92 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_release.py +88 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_repository.py +12 -1
- engineering_process-2.0.2/templates/PULL_REQUEST_TEMPLATE.md +0 -39
- {engineering_process-2.0.2 → engineering_process-2.2.0}/LICENSE +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/__main__.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_contract.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_posix.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_supervisor_windows.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/_windows_job.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/commands.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/helper_launch.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/production_engineering.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/project.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/release.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/requirements-dev.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/requirements-runtime.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/skills.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process/supervision.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/dependency_links.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/entry_points.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/requires.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/engineering_process.egg-info/top_level.txt +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process-graph.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/process-improve/SKILL.md +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/process_assets/skills/production-engineering/invariants.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/change.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/plan.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/process-graph.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/process-lock.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/production-engineering.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/project-legacy.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/project.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/receipt.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/release-change.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/release.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/schemas/review.schema.json +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/setup.cfg +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/AGENTS.process.md +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/adopt-process-windows-job.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/templates/adopt-process.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_commands.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_production_engineering.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_publication_compat.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_pypi_cache_horizon.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_skills.py +0 -0
- {engineering_process-2.0.2 → engineering_process-2.2.0}/tests/test_supervisor_posix.py +0 -0
{engineering_process-2.0.2/engineering_process.egg-info → engineering_process-2.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 2.0
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
505
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
481
|
-
|
|
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
|
|
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)
|
|
@@ -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
|
-
|
|
257
|
-
|
|
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"
|