engineering-process 1.2.6__tar.gz → 2.0.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-1.2.6/engineering_process.egg-info → engineering_process-2.0.0}/PKG-INFO +21 -5
- {engineering_process-1.2.6 → engineering_process-2.0.0}/README.md +20 -4
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/__init__.py +1 -1
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/skills.py +15 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0/engineering_process.egg-info}/PKG-INFO +21 -5
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/SOURCES.txt +8 -8
- {engineering_process-1.2.6 → engineering_process-2.0.0}/process-graph.json +23 -23
- {engineering_process-1.2.6/process_assets/skills/finish-change → engineering_process-2.0.0/process_assets/skills/change-complete}/SKILL.md +5 -4
- {engineering_process-1.2.6/process_assets/skills/implement-change → engineering_process-2.0.0/process_assets/skills/change-implement}/SKILL.md +3 -3
- {engineering_process-1.2.6/process_assets/skills/plan-change → engineering_process-2.0.0/process_assets/skills/change-plan}/SKILL.md +2 -2
- {engineering_process-1.2.6/process_assets/skills/review-change → engineering_process-2.0.0/process_assets/skills/change-review}/SKILL.md +15 -6
- {engineering_process-1.2.6/process_assets/skills/start-change → engineering_process-2.0.0/process_assets/skills/change-start}/SKILL.md +2 -2
- {engineering_process-1.2.6/process_assets/skills/verify-change → engineering_process-2.0.0/process_assets/skills/change-verify}/SKILL.md +3 -3
- {engineering_process-1.2.6/process_assets/skills/run-change → engineering_process-2.0.0/process_assets/skills/deliver-change}/SKILL.md +16 -15
- {engineering_process-1.2.6/process_assets/skills/improve-process → engineering_process-2.0.0/process_assets/skills/process-improve}/SKILL.md +2 -2
- {engineering_process-1.2.6 → engineering_process-2.0.0}/pyproject.toml +9 -9
- {engineering_process-1.2.6 → engineering_process-2.0.0}/templates/AGENTS.process.md +1 -1
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_adoption.py +83 -3
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_distribution.py +19 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_skills.py +64 -19
- {engineering_process-1.2.6 → engineering_process-2.0.0}/LICENSE +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/MANIFEST.in +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/__main__.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_contract.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_posix.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_windows.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_windows_job.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/adoption.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/cli.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/commands.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/contracts.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/distribution.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/helper_launch.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/lifecycle.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/production_engineering.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/project.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/publication_compat.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/release.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/repository.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/requirements-dev.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/requirements-runtime.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/supervision.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/dependency_links.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/entry_points.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/requires.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/top_level.txt +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/process_assets/skills/production-engineering/SKILL.md +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/process_assets/skills/production-engineering/invariants.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/change.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/plan.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/process-graph.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/process-lock.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/production-engineering.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/project-legacy.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/project.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/receipt.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/release-change.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/release.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/review.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/run.schema.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/setup.cfg +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/templates/adopt-process-windows-job.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/templates/adopt-process.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/templates/renovate.json +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_architecture.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_automation.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_cli.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_commands.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_contracts.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_lifecycle.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_production_engineering.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_publication_compat.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_pypi_cache_horizon.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_release.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_repository.py +0 -0
- {engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_supervisor_posix.py +0 -0
{engineering_process-1.2.6/engineering_process.egg-info → engineering_process-2.0.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -33,11 +33,27 @@ Portable skills explain what to do. processctl owns state transitions and curren
|
|
|
33
33
|
evidence. Each consumer owns its product rules, exact commands, merge policy, and
|
|
34
34
|
release decisions.
|
|
35
35
|
|
|
36
|
+
Start delivery work with [deliver-change](process_assets/skills/deliver-change/SKILL.md).
|
|
37
|
+
It selects the current phase from lifecycle state; callers do not select a phase skill:
|
|
38
|
+
|
|
39
|
+
deliver-change
|
|
40
|
+
change-start -> change-plan -> change-implement -> change-verify
|
|
41
|
+
-> change-review -> change-complete
|
|
42
|
+
|
|
43
|
+
[process-improve](process_assets/skills/process-improve/SKILL.md) handles a reusable
|
|
44
|
+
process problem, then returns delivery to the same lifecycle.
|
|
45
|
+
[production-engineering](process_assets/skills/production-engineering/SKILL.md) supplies
|
|
46
|
+
the invariant floor consulted from planning through independent review. Both are
|
|
47
|
+
reachable specializations; neither advances lifecycle state. `change-complete` calls
|
|
48
|
+
the existing `processctl change finish` command.
|
|
49
|
+
|
|
50
|
+
For migration from the previous skill identifiers, see [Versioning](VERSIONING.md#skill-namespace-migration).
|
|
51
|
+
|
|
36
52
|
## Architecture
|
|
37
53
|
|
|
38
54
|
The distribution has four live parts:
|
|
39
55
|
|
|
40
|
-
1. Managed skills under process_assets/skills, all reachable from
|
|
56
|
+
1. Managed skills under process_assets/skills, all reachable from deliver-change.
|
|
41
57
|
2. One processctl state machine under engineering_process/lifecycle.py.
|
|
42
58
|
3. JSON Schemas that are loaded directly by the runtime.
|
|
43
59
|
4. One adoption transaction that synchronizes managed skills and configuration from
|
|
@@ -175,7 +191,7 @@ This assessment is not a production certificate. Production still requires the
|
|
|
175
191
|
consumer's immutable readiness pack, every required capability in `enforced` state,
|
|
176
192
|
fresh consumer-owned verification on the exact candidate, and independent review.
|
|
177
193
|
|
|
178
|
-
For each ordinary change, `
|
|
194
|
+
For each ordinary change, `deliver-change` first surfaces this readiness view. The accepted
|
|
179
195
|
request and consumer rules determine which capabilities are affected. Every change
|
|
180
196
|
retains the project's baseline `requiredProfiles`; start and plan add any conditional
|
|
181
197
|
evidence profiles needed by affected capabilities and include a planned gap only when
|
|
@@ -184,7 +200,7 @@ floor. A planned-to-enforced promotion is a reviewed consumer source diff with f
|
|
|
184
200
|
evidence. Unrelated planned gaps remain visible but do not block development, and no
|
|
185
201
|
skill chooses product priorities or changes readiness automatically.
|
|
186
202
|
|
|
187
|
-
When a consumer incident exposes a reusable process gap, `improve
|
|
203
|
+
When a consumer incident exposes a reusable process gap, `process-improve` first keeps
|
|
188
204
|
the consumer safe, then prepares a sanitized GitHub issue draft from that checkout.
|
|
189
205
|
It deduplicates by consumer/process-version/invariant, requires owner authorization
|
|
190
206
|
before `gh issue create`, and uses an accepted issue as the later process change source
|
|
@@ -352,7 +368,7 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
352
368
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
353
369
|
the authority that started the relevant phase.
|
|
354
370
|
|
|
355
|
-
The [finding priority definitions](process_assets/skills/review
|
|
371
|
+
The [finding priority definitions](process_assets/skills/change-review/SKILL.md#finding-priority)
|
|
356
372
|
are the canonical P0-P3 impact convention for this process, including examples and
|
|
357
373
|
their relationship to blocking decisions.
|
|
358
374
|
|
|
@@ -9,11 +9,27 @@ Portable skills explain what to do. processctl owns state transitions and curren
|
|
|
9
9
|
evidence. Each consumer owns its product rules, exact commands, merge policy, and
|
|
10
10
|
release decisions.
|
|
11
11
|
|
|
12
|
+
Start delivery work with [deliver-change](process_assets/skills/deliver-change/SKILL.md).
|
|
13
|
+
It selects the current phase from lifecycle state; callers do not select a phase skill:
|
|
14
|
+
|
|
15
|
+
deliver-change
|
|
16
|
+
change-start -> change-plan -> change-implement -> change-verify
|
|
17
|
+
-> change-review -> change-complete
|
|
18
|
+
|
|
19
|
+
[process-improve](process_assets/skills/process-improve/SKILL.md) handles a reusable
|
|
20
|
+
process problem, then returns delivery to the same lifecycle.
|
|
21
|
+
[production-engineering](process_assets/skills/production-engineering/SKILL.md) supplies
|
|
22
|
+
the invariant floor consulted from planning through independent review. Both are
|
|
23
|
+
reachable specializations; neither advances lifecycle state. `change-complete` calls
|
|
24
|
+
the existing `processctl change finish` command.
|
|
25
|
+
|
|
26
|
+
For migration from the previous skill identifiers, see [Versioning](VERSIONING.md#skill-namespace-migration).
|
|
27
|
+
|
|
12
28
|
## Architecture
|
|
13
29
|
|
|
14
30
|
The distribution has four live parts:
|
|
15
31
|
|
|
16
|
-
1. Managed skills under process_assets/skills, all reachable from
|
|
32
|
+
1. Managed skills under process_assets/skills, all reachable from deliver-change.
|
|
17
33
|
2. One processctl state machine under engineering_process/lifecycle.py.
|
|
18
34
|
3. JSON Schemas that are loaded directly by the runtime.
|
|
19
35
|
4. One adoption transaction that synchronizes managed skills and configuration from
|
|
@@ -151,7 +167,7 @@ This assessment is not a production certificate. Production still requires the
|
|
|
151
167
|
consumer's immutable readiness pack, every required capability in `enforced` state,
|
|
152
168
|
fresh consumer-owned verification on the exact candidate, and independent review.
|
|
153
169
|
|
|
154
|
-
For each ordinary change, `
|
|
170
|
+
For each ordinary change, `deliver-change` first surfaces this readiness view. The accepted
|
|
155
171
|
request and consumer rules determine which capabilities are affected. Every change
|
|
156
172
|
retains the project's baseline `requiredProfiles`; start and plan add any conditional
|
|
157
173
|
evidence profiles needed by affected capabilities and include a planned gap only when
|
|
@@ -160,7 +176,7 @@ floor. A planned-to-enforced promotion is a reviewed consumer source diff with f
|
|
|
160
176
|
evidence. Unrelated planned gaps remain visible but do not block development, and no
|
|
161
177
|
skill chooses product priorities or changes readiness automatically.
|
|
162
178
|
|
|
163
|
-
When a consumer incident exposes a reusable process gap, `improve
|
|
179
|
+
When a consumer incident exposes a reusable process gap, `process-improve` first keeps
|
|
164
180
|
the consumer safe, then prepares a sanitized GitHub issue draft from that checkout.
|
|
165
181
|
It deduplicates by consumer/process-version/invariant, requires owner authorization
|
|
166
182
|
before `gh issue create`, and uses an accepted issue as the later process change source
|
|
@@ -328,7 +344,7 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
328
344
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
329
345
|
the authority that started the relevant phase.
|
|
330
346
|
|
|
331
|
-
The [finding priority definitions](process_assets/skills/review
|
|
347
|
+
The [finding priority definitions](process_assets/skills/change-review/SKILL.md#finding-priority)
|
|
332
348
|
are the canonical P0-P3 impact convention for this process, including examples and
|
|
333
349
|
their relationship to blocking decisions.
|
|
334
350
|
|
|
@@ -90,6 +90,21 @@ def validate_skills(
|
|
|
90
90
|
graph = load_and_validate(
|
|
91
91
|
graph_path, "process-graph", schema_root=schemas_root(process_root)
|
|
92
92
|
)
|
|
93
|
+
owners = {state["id"]: state["ownerSkill"] for state in graph["states"]}
|
|
94
|
+
if len(owners) != len(graph["states"]):
|
|
95
|
+
raise ProcessError("process graph state ids must be unique")
|
|
96
|
+
for state in graph["states"]:
|
|
97
|
+
for transition in state["transitions"]:
|
|
98
|
+
destination = transition["nextState"]
|
|
99
|
+
skill = transition["nextSkill"]
|
|
100
|
+
if (destination is None) != (skill is None):
|
|
101
|
+
raise ProcessError("process graph terminal transitions must have both targets null")
|
|
102
|
+
if destination is not None and destination not in owners:
|
|
103
|
+
raise ProcessError(f"process graph references missing state: {destination}")
|
|
104
|
+
if skill is not None and skill not in names:
|
|
105
|
+
raise ProcessError(
|
|
106
|
+
f"process graph transition from {state['id']} references missing skill: {skill}"
|
|
107
|
+
)
|
|
93
108
|
routed = {graph["entrySkill"]}
|
|
94
109
|
routed.update(state["ownerSkill"] for state in graph["states"])
|
|
95
110
|
routed.update(graph.get("specializations", {}).values())
|
{engineering_process-1.2.6 → engineering_process-2.0.0/engineering_process.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -33,11 +33,27 @@ Portable skills explain what to do. processctl owns state transitions and curren
|
|
|
33
33
|
evidence. Each consumer owns its product rules, exact commands, merge policy, and
|
|
34
34
|
release decisions.
|
|
35
35
|
|
|
36
|
+
Start delivery work with [deliver-change](process_assets/skills/deliver-change/SKILL.md).
|
|
37
|
+
It selects the current phase from lifecycle state; callers do not select a phase skill:
|
|
38
|
+
|
|
39
|
+
deliver-change
|
|
40
|
+
change-start -> change-plan -> change-implement -> change-verify
|
|
41
|
+
-> change-review -> change-complete
|
|
42
|
+
|
|
43
|
+
[process-improve](process_assets/skills/process-improve/SKILL.md) handles a reusable
|
|
44
|
+
process problem, then returns delivery to the same lifecycle.
|
|
45
|
+
[production-engineering](process_assets/skills/production-engineering/SKILL.md) supplies
|
|
46
|
+
the invariant floor consulted from planning through independent review. Both are
|
|
47
|
+
reachable specializations; neither advances lifecycle state. `change-complete` calls
|
|
48
|
+
the existing `processctl change finish` command.
|
|
49
|
+
|
|
50
|
+
For migration from the previous skill identifiers, see [Versioning](VERSIONING.md#skill-namespace-migration).
|
|
51
|
+
|
|
36
52
|
## Architecture
|
|
37
53
|
|
|
38
54
|
The distribution has four live parts:
|
|
39
55
|
|
|
40
|
-
1. Managed skills under process_assets/skills, all reachable from
|
|
56
|
+
1. Managed skills under process_assets/skills, all reachable from deliver-change.
|
|
41
57
|
2. One processctl state machine under engineering_process/lifecycle.py.
|
|
42
58
|
3. JSON Schemas that are loaded directly by the runtime.
|
|
43
59
|
4. One adoption transaction that synchronizes managed skills and configuration from
|
|
@@ -175,7 +191,7 @@ This assessment is not a production certificate. Production still requires the
|
|
|
175
191
|
consumer's immutable readiness pack, every required capability in `enforced` state,
|
|
176
192
|
fresh consumer-owned verification on the exact candidate, and independent review.
|
|
177
193
|
|
|
178
|
-
For each ordinary change, `
|
|
194
|
+
For each ordinary change, `deliver-change` first surfaces this readiness view. The accepted
|
|
179
195
|
request and consumer rules determine which capabilities are affected. Every change
|
|
180
196
|
retains the project's baseline `requiredProfiles`; start and plan add any conditional
|
|
181
197
|
evidence profiles needed by affected capabilities and include a planned gap only when
|
|
@@ -184,7 +200,7 @@ floor. A planned-to-enforced promotion is a reviewed consumer source diff with f
|
|
|
184
200
|
evidence. Unrelated planned gaps remain visible but do not block development, and no
|
|
185
201
|
skill chooses product priorities or changes readiness automatically.
|
|
186
202
|
|
|
187
|
-
When a consumer incident exposes a reusable process gap, `improve
|
|
203
|
+
When a consumer incident exposes a reusable process gap, `process-improve` first keeps
|
|
188
204
|
the consumer safe, then prepares a sanitized GitHub issue draft from that checkout.
|
|
189
205
|
It deduplicates by consumer/process-version/invariant, requires owner authorization
|
|
190
206
|
before `gh issue create`, and uses an accepted issue as the later process change source
|
|
@@ -352,7 +368,7 @@ URL; without it, the review remains pending. Earlier plan and review documents r
|
|
|
352
368
|
readable, and their runs remain registrable or finishable with the version selected by
|
|
353
369
|
the authority that started the relevant phase.
|
|
354
370
|
|
|
355
|
-
The [finding priority definitions](process_assets/skills/review
|
|
371
|
+
The [finding priority definitions](process_assets/skills/change-review/SKILL.md#finding-priority)
|
|
356
372
|
are the canonical P0-P3 impact convention for this process, including examples and
|
|
357
373
|
their relationship to blocking decisions.
|
|
358
374
|
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/SOURCES.txt
RENAMED
|
@@ -31,16 +31,16 @@ engineering_process.egg-info/dependency_links.txt
|
|
|
31
31
|
engineering_process.egg-info/entry_points.txt
|
|
32
32
|
engineering_process.egg-info/requires.txt
|
|
33
33
|
engineering_process.egg-info/top_level.txt
|
|
34
|
-
process_assets/skills/
|
|
35
|
-
process_assets/skills/implement
|
|
36
|
-
process_assets/skills/
|
|
37
|
-
process_assets/skills/
|
|
34
|
+
process_assets/skills/change-complete/SKILL.md
|
|
35
|
+
process_assets/skills/change-implement/SKILL.md
|
|
36
|
+
process_assets/skills/change-plan/SKILL.md
|
|
37
|
+
process_assets/skills/change-review/SKILL.md
|
|
38
|
+
process_assets/skills/change-start/SKILL.md
|
|
39
|
+
process_assets/skills/change-verify/SKILL.md
|
|
40
|
+
process_assets/skills/deliver-change/SKILL.md
|
|
41
|
+
process_assets/skills/process-improve/SKILL.md
|
|
38
42
|
process_assets/skills/production-engineering/SKILL.md
|
|
39
43
|
process_assets/skills/production-engineering/invariants.json
|
|
40
|
-
process_assets/skills/review-change/SKILL.md
|
|
41
|
-
process_assets/skills/run-change/SKILL.md
|
|
42
|
-
process_assets/skills/start-change/SKILL.md
|
|
43
|
-
process_assets/skills/verify-change/SKILL.md
|
|
44
44
|
schemas/change.schema.json
|
|
45
45
|
schemas/plan.schema.json
|
|
46
46
|
schemas/process-graph.schema.json
|
|
@@ -1,83 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"entrySkill": "
|
|
3
|
+
"entrySkill": "deliver-change",
|
|
4
4
|
"specializations": {
|
|
5
|
-
"processChange": "improve
|
|
5
|
+
"processChange": "process-improve",
|
|
6
6
|
"productionEngineering": "production-engineering"
|
|
7
7
|
},
|
|
8
8
|
"states": [
|
|
9
9
|
{
|
|
10
10
|
"id": "unregistered",
|
|
11
|
-
"ownerSkill": "start
|
|
11
|
+
"ownerSkill": "change-start",
|
|
12
12
|
"commands": ["change start"],
|
|
13
13
|
"transitions": [
|
|
14
|
-
{"result": "success", "nextState": "specified", "nextSkill": "plan
|
|
14
|
+
{"result": "success", "nextState": "specified", "nextSkill": "change-plan"}
|
|
15
15
|
]
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"id": "specified",
|
|
19
|
-
"ownerSkill": "plan
|
|
19
|
+
"ownerSkill": "change-plan",
|
|
20
20
|
"commands": ["change plan"],
|
|
21
21
|
"transitions": [
|
|
22
|
-
{"result": "success", "nextState": "planned", "nextSkill": "implement
|
|
22
|
+
{"result": "success", "nextState": "planned", "nextSkill": "change-implement"}
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"id": "planned",
|
|
27
|
-
"ownerSkill": "implement
|
|
27
|
+
"ownerSkill": "change-implement",
|
|
28
28
|
"commands": ["change implement"],
|
|
29
29
|
"transitions": [
|
|
30
|
-
{"result": "success", "nextState": "implementing", "nextSkill": "verify
|
|
30
|
+
{"result": "success", "nextState": "implementing", "nextSkill": "change-verify"}
|
|
31
31
|
]
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
"id": "implementing",
|
|
35
|
-
"ownerSkill": "verify
|
|
35
|
+
"ownerSkill": "change-verify",
|
|
36
36
|
"commands": ["change implement", "change verify"],
|
|
37
37
|
"transitions": [
|
|
38
|
-
{"result": "profile-passed", "nextState": "implementing", "nextSkill": "verify
|
|
39
|
-
{"result": "all-passed", "nextState": "verified", "nextSkill": "review
|
|
38
|
+
{"result": "profile-passed", "nextState": "implementing", "nextSkill": "change-verify"},
|
|
39
|
+
{"result": "all-passed", "nextState": "verified", "nextSkill": "change-review"}
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"id": "verified",
|
|
44
|
-
"ownerSkill": "review
|
|
44
|
+
"ownerSkill": "change-review",
|
|
45
45
|
"commands": ["change implement", "change review start"],
|
|
46
46
|
"transitions": [
|
|
47
|
-
{"result": "assigned", "nextState": "review-pending", "nextSkill": "review
|
|
48
|
-
{"result": "source-changed", "nextState": "implementing", "nextSkill": "implement
|
|
47
|
+
{"result": "assigned", "nextState": "review-pending", "nextSkill": "change-review"},
|
|
48
|
+
{"result": "source-changed", "nextState": "implementing", "nextSkill": "change-implement"}
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"id": "review-pending",
|
|
53
|
-
"ownerSkill": "review
|
|
53
|
+
"ownerSkill": "change-review",
|
|
54
54
|
"commands": ["change implement", "change review submit"],
|
|
55
55
|
"transitions": [
|
|
56
|
-
{"result": "approved", "nextState": "approved", "nextSkill": "
|
|
57
|
-
{"result": "changes-requested", "nextState": "changes-requested", "nextSkill": "implement
|
|
58
|
-
{"result": "source-changed", "nextState": "implementing", "nextSkill": "implement
|
|
56
|
+
{"result": "approved", "nextState": "approved", "nextSkill": "change-complete"},
|
|
57
|
+
{"result": "changes-requested", "nextState": "changes-requested", "nextSkill": "change-implement"},
|
|
58
|
+
{"result": "source-changed", "nextState": "implementing", "nextSkill": "change-implement"}
|
|
59
59
|
]
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"id": "changes-requested",
|
|
63
|
-
"ownerSkill": "implement
|
|
63
|
+
"ownerSkill": "change-implement",
|
|
64
64
|
"commands": ["change implement"],
|
|
65
65
|
"transitions": [
|
|
66
|
-
{"result": "success", "nextState": "implementing", "nextSkill": "verify
|
|
66
|
+
{"result": "success", "nextState": "implementing", "nextSkill": "change-verify"}
|
|
67
67
|
]
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"id": "approved",
|
|
71
|
-
"ownerSkill": "
|
|
71
|
+
"ownerSkill": "change-complete",
|
|
72
72
|
"commands": ["change finish", "change implement"],
|
|
73
73
|
"transitions": [
|
|
74
74
|
{"result": "success", "nextState": null, "nextSkill": null},
|
|
75
|
-
{"result": "source-changed", "nextState": "implementing", "nextSkill": "implement
|
|
75
|
+
{"result": "source-changed", "nextState": "implementing", "nextSkill": "change-implement"}
|
|
76
76
|
]
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"id": "blocked",
|
|
80
|
-
"ownerSkill": "
|
|
80
|
+
"ownerSkill": "deliver-change",
|
|
81
81
|
"commands": [],
|
|
82
82
|
"transitions": []
|
|
83
83
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Complete an approved change only while
|
|
2
|
+
name: change-complete
|
|
3
|
+
description: Complete an approved change when routed by deliver-change, only while verification and independent review still match the repository snapshot.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Complete a change
|
|
7
7
|
|
|
8
8
|
Confirm the lifecycle is approved, every required profile passed, every blocking
|
|
9
9
|
finding is closed, every non-blocking finding has its required disposition, and the
|
|
@@ -11,7 +11,8 @@ repository still matches the reviewed snapshot. Then run:
|
|
|
11
11
|
|
|
12
12
|
processctl change finish --change-id ID --actor ACTOR --context CONTEXT
|
|
13
13
|
|
|
14
|
-
The
|
|
14
|
+
The existing `change finish` CLI operation writes one bounded completion receipt and
|
|
15
|
+
marks the run completed.
|
|
15
16
|
Completion does not itself grant merge, deployment, or release authority; those
|
|
16
17
|
remain project-owned operations. Never report completion from prose alone.
|
|
17
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: implement
|
|
3
|
-
description: Implement the accepted plan or resolve blocking review findings without changing the contract implicitly.
|
|
2
|
+
name: change-implement
|
|
3
|
+
description: Implement the accepted plan or resolve blocking review findings when routed by deliver-change, without changing the contract implicitly.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Implement a change
|
|
@@ -29,4 +29,4 @@ gaps merely because they are listed.
|
|
|
29
29
|
|
|
30
30
|
When evidence exposes a contract gap, stop and ask the project owner to supersede the
|
|
31
31
|
contract. Do not make review prose into new scope. When implementation is ready,
|
|
32
|
-
route to **verify
|
|
32
|
+
route to **change-verify**.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: plan
|
|
3
|
-
description:
|
|
2
|
+
name: change-plan
|
|
3
|
+
description: Plan the registered change and its verification boundary when deliver-change routes a specified change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Plan a change
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: review
|
|
3
|
-
description: Review the exact verified snapshot from an actor and context
|
|
2
|
+
name: change-review
|
|
3
|
+
description: Review the exact verified snapshot from an independent actor and context when routed by deliver-change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Review a change
|
|
7
7
|
|
|
8
8
|
The reviewer must not share either actor identity or execution context with an
|
|
9
|
-
implementer in the current cycle.
|
|
9
|
+
implementer in the current cycle. Read `processctl change status --change-id ID`.
|
|
10
|
+
When the phase is `verified`, start the assignment:
|
|
10
11
|
|
|
11
12
|
processctl change review start --change-id ID --actor REVIEWER --context REVIEW_CONTEXT
|
|
12
13
|
|
|
14
|
+
When the phase is `review-pending`, resume the existing assignment; do not run
|
|
15
|
+
`change review start` again. Read `.process/runs/ID/run.json` for its `cycle` and
|
|
16
|
+
`reviewAssignment`, including the assigned reviewer, checkpoint, and
|
|
17
|
+
`reportSchemaVersion`. Continue with the assigned independent actor/context and the
|
|
18
|
+
existing report path, `.process/runs/ID/review-CYCLE.json`. If that reviewer is
|
|
19
|
+
unavailable, report the pending assignment as a blocker; never impersonate its
|
|
20
|
+
identity or create a replacement assignment from another context.
|
|
21
|
+
|
|
13
22
|
Review the accepted contract, plan, complete diff, focused tests, and verification
|
|
14
23
|
evidence. The first pass is comprehensive within that frozen contract. Every finding
|
|
15
24
|
maps to one accepted criterion and records priority, origin, severity, and location.
|
|
@@ -41,7 +50,7 @@ consider consumer evidence that the lifecycle cannot observe. Every schema-versi
|
|
|
41
50
|
report classifies `processImprovement` as `none`, `consumer-specific`, or
|
|
42
51
|
`shared-process` and gives a concrete rationale. Consumer-specific behavior stays in
|
|
43
52
|
the consumer. For `shared-process`, keep the assignment `review-pending` and route the
|
|
44
|
-
candidate through **improve
|
|
53
|
+
candidate through **process-improve**. Submit only after an existing or owner-authorized
|
|
45
54
|
issue supplies the stable HTTPS `recordUrl`; the review itself remains read-only.
|
|
46
55
|
|
|
47
56
|
Read the consumer readiness result and repository rules. Check the complete diff for
|
|
@@ -56,8 +65,8 @@ Validate and submit the report:
|
|
|
56
65
|
processctl contract validate --kind review REPORT_PATH
|
|
57
66
|
processctl change review submit --change-id ID --review REPORT_PATH
|
|
58
67
|
|
|
59
|
-
Review is read-only. Requested changes route back to **implement
|
|
60
|
-
routes to **
|
|
68
|
+
Review is read-only. Requested changes route back to **change-implement**; approval
|
|
69
|
+
routes to **change-complete**. Keep the same independent reviewer for corrections.
|
|
61
70
|
Follow-up scope is only carried findings, remediation diffs, and regressions against
|
|
62
71
|
the frozen contract. A new blocker must be either remediation-caused or a P0/P1 late
|
|
63
72
|
violation with a rationale. After two correction cycles, another changes-requested
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: start
|
|
3
|
-
description: Turn an accepted request into a bounded change contract
|
|
2
|
+
name: change-start
|
|
3
|
+
description: Turn an accepted request into a bounded change contract when deliver-change routes a new change to lifecycle start.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Start a change
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: verify
|
|
3
|
-
description: Run the project-owned verification profiles
|
|
2
|
+
name: change-verify
|
|
3
|
+
description: Run the project-owned verification profiles on one unchanged repository snapshot when routed by deliver-change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Verify a change
|
|
@@ -23,4 +23,4 @@ planned capability whose gap remains open. A readiness promotion is valid only w
|
|
|
23
23
|
all evidence named by that capability passes on this same snapshot.
|
|
24
24
|
|
|
25
25
|
When all required profiles pass on the same snapshot, the lifecycle becomes verified;
|
|
26
|
-
route to **review
|
|
26
|
+
route to **change-review**.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description:
|
|
2
|
+
name: deliver-change
|
|
3
|
+
description: Drive a repository change through the governed engineering lifecycle, from an accepted contract to independently reviewed completion.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Deliver a change
|
|
7
7
|
|
|
8
8
|
Use this as the only entry point for delivery work. Run `processctl project validate
|
|
9
9
|
--json` first. When readiness is present, report its stage, immutable pack versions,
|
|
@@ -11,21 +11,22 @@ enforced floor, and planned gaps. Planned gaps guide future work but do not beco
|
|
|
11
11
|
scope of the current change unless the accepted request selects one; never choose the
|
|
12
12
|
product roadmap autonomously.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
current phase:
|
|
16
|
-
|
|
17
|
-
1. No run: use **start
|
|
18
|
-
2. specified: use **plan
|
|
19
|
-
3. planned or changes-requested: use **implement
|
|
20
|
-
4. implementing: use **verify
|
|
21
|
-
5. verified or review-pending: use **review
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
The six **change-*** skills are lifecycle routes selected here. Inspect processctl
|
|
15
|
+
change status when a change already exists, then route exactly one current phase:
|
|
16
|
+
|
|
17
|
+
1. No run: use **change-start**.
|
|
18
|
+
2. specified: use **change-plan**.
|
|
19
|
+
3. planned or changes-requested: use **change-implement**.
|
|
20
|
+
4. implementing: use **change-verify**.
|
|
21
|
+
5. verified or review-pending: use **change-review**, resuming the existing assignment
|
|
22
|
+
when review is pending; if the repository changed after evidence was recorded,
|
|
23
|
+
use **change-implement** to open a new cycle.
|
|
24
|
+
6. approved: use **change-complete**; a later repository change also reopens through
|
|
25
|
+
**change-implement**.
|
|
25
26
|
7. blocked: stop. The current contract cannot merge; the owner may narrow or
|
|
26
27
|
supersede it, but no correction-limit stop can waive independent review.
|
|
27
28
|
|
|
28
|
-
When changing this process itself, first use **improve
|
|
29
|
+
When changing this process itself, first use **process-improve** to prove the request
|
|
29
30
|
came from a real consumer incident or need; the change still follows the same six
|
|
30
31
|
phases afterward.
|
|
31
32
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: improve
|
|
2
|
+
name: process-improve
|
|
3
3
|
description: Change the shared process only in response to evidence from a real consumer and prefer removing complexity over adding governance.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -68,7 +68,7 @@ Find the smallest reusable correction. Prefer, in order:
|
|
|
68
68
|
4. add a new gate only when the consumer evidence proves the other options cannot
|
|
69
69
|
protect the required invariant.
|
|
70
70
|
|
|
71
|
-
Use **
|
|
71
|
+
Use **deliver-change** for the actual work and require the same independent final review
|
|
72
72
|
as any consumer change. Track cross-repository discussion in ordinary issues or pull
|
|
73
73
|
requests; do not create a second lifecycle or evidence federation. The process never
|
|
74
74
|
self-publishes or self-merges: the owner retains release and adoption authority, and
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "engineering-process"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.0"
|
|
8
8
|
description = "A small agent-neutral engineering lifecycle with managed adoption"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -60,15 +60,15 @@ dev = {file = ["engineering_process/requirements-dev.txt"]}
|
|
|
60
60
|
"schemas/review.schema.json",
|
|
61
61
|
"schemas/run.schema.json",
|
|
62
62
|
]
|
|
63
|
-
"share/engineering-process/skills/
|
|
64
|
-
"share/engineering-process/skills/implement
|
|
65
|
-
"share/engineering-process/skills/
|
|
66
|
-
"share/engineering-process/skills/
|
|
63
|
+
"share/engineering-process/skills/change-complete" = ["process_assets/skills/change-complete/SKILL.md"]
|
|
64
|
+
"share/engineering-process/skills/change-implement" = ["process_assets/skills/change-implement/SKILL.md"]
|
|
65
|
+
"share/engineering-process/skills/change-plan" = ["process_assets/skills/change-plan/SKILL.md"]
|
|
66
|
+
"share/engineering-process/skills/change-review" = ["process_assets/skills/change-review/SKILL.md"]
|
|
67
|
+
"share/engineering-process/skills/change-start" = ["process_assets/skills/change-start/SKILL.md"]
|
|
68
|
+
"share/engineering-process/skills/change-verify" = ["process_assets/skills/change-verify/SKILL.md"]
|
|
69
|
+
"share/engineering-process/skills/deliver-change" = ["process_assets/skills/deliver-change/SKILL.md"]
|
|
70
|
+
"share/engineering-process/skills/process-improve" = ["process_assets/skills/process-improve/SKILL.md"]
|
|
67
71
|
"share/engineering-process/skills/production-engineering" = [
|
|
68
72
|
"process_assets/skills/production-engineering/SKILL.md",
|
|
69
73
|
"process_assets/skills/production-engineering/invariants.json",
|
|
70
74
|
]
|
|
71
|
-
"share/engineering-process/skills/review-change" = ["process_assets/skills/review-change/SKILL.md"]
|
|
72
|
-
"share/engineering-process/skills/run-change" = ["process_assets/skills/run-change/SKILL.md"]
|
|
73
|
-
"share/engineering-process/skills/start-change" = ["process_assets/skills/start-change/SKILL.md"]
|
|
74
|
-
"share/engineering-process/skills/verify-change" = ["process_assets/skills/verify-change/SKILL.md"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- engineering-process:start -->
|
|
2
2
|
## Engineering process
|
|
3
3
|
|
|
4
|
-
For non-trivial delivery work, enter through the managed
|
|
4
|
+
For non-trivial delivery work, enter through the managed deliver-change skill and follow
|
|
5
5
|
the processctl lifecycle: start, plan, implement, verify, independent review, finish.
|
|
6
6
|
|
|
7
7
|
This repository owns product decisions, domain rules, exact argument-array commands,
|
|
@@ -17,6 +17,11 @@ from engineering_process.contracts import ProcessError, read_json
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
PROCESS_ROOT = Path(__file__).resolve().parent.parent
|
|
20
|
+
PREVIOUS_SKILLS = (
|
|
21
|
+
"run-change", "start-change", "plan-change", "implement-change",
|
|
22
|
+
"verify-change", "review-change", "finish-change", "improve-process",
|
|
23
|
+
"production-engineering",
|
|
24
|
+
)
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
def write_json(path: Path, value: object) -> None:
|
|
@@ -135,6 +140,80 @@ class AdoptionTests(unittest.TestCase):
|
|
|
135
140
|
def tearDown(self) -> None:
|
|
136
141
|
self.temporary.cleanup()
|
|
137
142
|
|
|
143
|
+
def previous_catalog_consumer(self) -> Path:
|
|
144
|
+
consumer = (self.root / "previous-catalog").resolve()
|
|
145
|
+
write_json(consumer / ".process" / "project.json", read_json(self.root / ".process" / "project.json"))
|
|
146
|
+
managed = []
|
|
147
|
+
for name in PREVIOUS_SKILLS:
|
|
148
|
+
directory = consumer / ".agents" / "skills" / name
|
|
149
|
+
directory.mkdir(parents=True)
|
|
150
|
+
(directory / "SKILL.md").write_bytes(
|
|
151
|
+
f"---\nname: {name}\ndescription: Previous release skill.\n---\n".encode("utf-8")
|
|
152
|
+
)
|
|
153
|
+
write_json(directory / ".engineering-process.json", {
|
|
154
|
+
"schemaVersion": 1, "managedBy": "engineering-process",
|
|
155
|
+
"version": "1.2.6", "digest": "sha256:" + "0" * 64,
|
|
156
|
+
})
|
|
157
|
+
(directory / "consumer-notes.md").write_bytes(b"consumer owned\n")
|
|
158
|
+
managed.extend(f".agents/skills/{name}/{file}" for file in ("SKILL.md", ".engineering-process.json"))
|
|
159
|
+
invariant = Path(".agents/skills/production-engineering/invariants.json")
|
|
160
|
+
(consumer / invariant).write_bytes(
|
|
161
|
+
(PROCESS_ROOT / "process_assets" / "skills" / "production-engineering" / "invariants.json").read_bytes()
|
|
162
|
+
)
|
|
163
|
+
managed.append(invariant.as_posix())
|
|
164
|
+
write_json(consumer / ".process" / "process.lock", {
|
|
165
|
+
"schemaVersion": 2,
|
|
166
|
+
"process": {"package": "engineering-process", "version": "1.2.6", "digest": "sha256:" + "0" * 64},
|
|
167
|
+
"requirementsDigest": "sha256:" + "1" * 64,
|
|
168
|
+
"skills": list(PREVIOUS_SKILLS), "managedFiles": managed,
|
|
169
|
+
})
|
|
170
|
+
(consumer / "AGENTS.md").write_bytes(
|
|
171
|
+
b"# Consumer rules\n\n<!-- engineering-process:start -->\nUse run-change.\n<!-- engineering-process:end -->\n"
|
|
172
|
+
)
|
|
173
|
+
return consumer
|
|
174
|
+
|
|
175
|
+
def test_previous_catalog_migrates_all_eight_names_and_is_idempotent(self) -> None:
|
|
176
|
+
consumer = self.previous_catalog_consumer()
|
|
177
|
+
self.assertEqual("applied", apply_adoption(consumer, PROCESS_ROOT, self.requirements)["status"])
|
|
178
|
+
lock = read_json(consumer / ".process" / "process.lock")
|
|
179
|
+
expected = {path.name for path in (PROCESS_ROOT / "process_assets" / "skills").iterdir() if path.is_dir()}
|
|
180
|
+
self.assertEqual(expected, set(lock["skills"]))
|
|
181
|
+
installed = consumer / ".agents" / "skills"
|
|
182
|
+
self.assertEqual(expected, {path.name for path in installed.iterdir() if (path / "SKILL.md").is_file()})
|
|
183
|
+
for name in PREVIOUS_SKILLS:
|
|
184
|
+
with self.subTest(name=name):
|
|
185
|
+
self.assertEqual(b"consumer owned\n", (installed / name / "consumer-notes.md").read_bytes())
|
|
186
|
+
if name != "production-engineering":
|
|
187
|
+
for filename in ("SKILL.md", ".engineering-process.json"):
|
|
188
|
+
self.assertFalse((installed / name / filename).exists())
|
|
189
|
+
self.assertNotIn(f".agents/skills/{name}/{filename}", lock["managedFiles"])
|
|
190
|
+
instructions = (consumer / "AGENTS.md").read_text(encoding="utf-8")
|
|
191
|
+
self.assertIn("# Consumer rules", instructions)
|
|
192
|
+
self.assertIn("deliver-change", instructions)
|
|
193
|
+
self.assertNotIn("run-change", instructions)
|
|
194
|
+
self.assertEqual("passed", check_adoption(consumer, PROCESS_ROOT, self.requirements)["status"])
|
|
195
|
+
self.assertEqual("unchanged", apply_adoption(consumer, PROCESS_ROOT, self.requirements)["status"])
|
|
196
|
+
|
|
197
|
+
def test_previous_catalog_is_restored_after_rename_cleanup_failure(self) -> None:
|
|
198
|
+
consumer = self.previous_catalog_consumer()
|
|
199
|
+
before = {path.relative_to(consumer): path.read_bytes() for path in consumer.rglob("*") if path.is_file()}
|
|
200
|
+
original_unlink = Path.unlink
|
|
201
|
+
failed = False
|
|
202
|
+
|
|
203
|
+
def fail_old_skill_removal(path: Path, *args: object, **kwargs: object) -> None:
|
|
204
|
+
nonlocal failed
|
|
205
|
+
if not failed and path == consumer / ".agents" / "skills" / "run-change" / "SKILL.md":
|
|
206
|
+
failed = True
|
|
207
|
+
raise OSError("injected old-skill cleanup failure")
|
|
208
|
+
original_unlink(path, *args, **kwargs)
|
|
209
|
+
|
|
210
|
+
with mock.patch.object(Path, "unlink", fail_old_skill_removal), self.assertRaisesRegex(ProcessError, "was rolled back"):
|
|
211
|
+
apply_adoption(consumer, PROCESS_ROOT, self.requirements)
|
|
212
|
+
self.assertTrue(failed)
|
|
213
|
+
self.assertEqual(before, {path.relative_to(consumer): path.read_bytes() for path in consumer.rglob("*") if path.is_file()})
|
|
214
|
+
self.assertEqual("applied", apply_adoption(consumer, PROCESS_ROOT, self.requirements)["status"])
|
|
215
|
+
self.assertEqual("passed", check_adoption(consumer, PROCESS_ROOT, self.requirements)["status"])
|
|
216
|
+
|
|
138
217
|
def test_legacy_consumer_converges_and_second_apply_is_noop(self) -> None:
|
|
139
218
|
first = apply_adoption(
|
|
140
219
|
self.root, PROCESS_ROOT, self.requirements, requirements_source=self.requirements
|
|
@@ -160,7 +239,8 @@ class AdoptionTests(unittest.TestCase):
|
|
|
160
239
|
self.assertFalse((self.root / ".process" / "automation.json").exists())
|
|
161
240
|
self.assertFalse((self.root / ".process" / "adoption-migrations").exists())
|
|
162
241
|
self.assertTrue((self.root / ".agents" / "skills" / "consumer-owned" / "SKILL.md").is_file())
|
|
163
|
-
self.assertTrue((self.root / ".agents" / "skills" / "improve
|
|
242
|
+
self.assertTrue((self.root / ".agents" / "skills" / "process-improve" / "SKILL.md").is_file())
|
|
243
|
+
self.assertFalse((self.root / ".agents" / "skills" / "run-change" / "SKILL.md").exists())
|
|
164
244
|
self.assertEqual(
|
|
165
245
|
(
|
|
166
246
|
PROCESS_ROOT
|
|
@@ -204,7 +284,7 @@ class AdoptionTests(unittest.TestCase):
|
|
|
204
284
|
lock = read_json(self.root / ".process" / "process.lock")
|
|
205
285
|
self.assertEqual(2, lock["schemaVersion"])
|
|
206
286
|
self.assertEqual(VERSION, lock["process"]["version"])
|
|
207
|
-
self.assertIn(".agents/skills/
|
|
287
|
+
self.assertIn(".agents/skills/deliver-change/SKILL.md", lock["managedFiles"])
|
|
208
288
|
self.assertIn(".process/adopt-process-windows-job.py", lock["managedFiles"])
|
|
209
289
|
|
|
210
290
|
second = apply_adoption(self.root, PROCESS_ROOT, self.requirements)
|
|
@@ -220,7 +300,7 @@ class AdoptionTests(unittest.TestCase):
|
|
|
220
300
|
apply_adoption(self.root, PROCESS_ROOT, self.requirements)
|
|
221
301
|
|
|
222
302
|
def test_consumer_owned_skill_name_collision_fails_closed(self) -> None:
|
|
223
|
-
collision = self.root / ".agents" / "skills" / "improve
|
|
303
|
+
collision = self.root / ".agents" / "skills" / "process-improve" / "SKILL.md"
|
|
224
304
|
collision.parent.mkdir(parents=True)
|
|
225
305
|
collision.write_text("consumer skill\n", encoding="utf-8")
|
|
226
306
|
with self.assertRaisesRegex(ProcessError, "consumer-owned path collides"):
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import hashlib
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import tempfile
|
|
6
|
+
import tomllib
|
|
6
7
|
import unittest
|
|
7
8
|
|
|
8
9
|
from engineering_process.distribution import distribution_digest, skill_digest
|
|
@@ -20,6 +21,24 @@ def framed_digest(entries: list[tuple[str, bytes]]) -> str:
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class DistributionTests(unittest.TestCase):
|
|
24
|
+
def test_packaged_skill_assets_match_the_source_catalog(self) -> None:
|
|
25
|
+
root = Path(__file__).resolve().parent.parent
|
|
26
|
+
metadata = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8"))
|
|
27
|
+
declared = {
|
|
28
|
+
target: set(paths)
|
|
29
|
+
for target, paths in metadata["tool"]["setuptools"]["data-files"].items()
|
|
30
|
+
if target.startswith("share/engineering-process/skills/")
|
|
31
|
+
}
|
|
32
|
+
expected = {
|
|
33
|
+
f"share/engineering-process/skills/{directory.name}": {
|
|
34
|
+
path.relative_to(root).as_posix()
|
|
35
|
+
for path in directory.rglob("*") if path.is_file()
|
|
36
|
+
}
|
|
37
|
+
for directory in (root / "process_assets" / "skills").iterdir()
|
|
38
|
+
if directory.is_dir()
|
|
39
|
+
}
|
|
40
|
+
self.assertEqual(expected, declared)
|
|
41
|
+
|
|
23
42
|
def test_digests_follow_canonical_relative_posix_order(self) -> None:
|
|
24
43
|
entries = [
|
|
25
44
|
("skills/sample/SKILL.md", b"skill\n"),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from copy import deepcopy
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
import re
|
|
4
6
|
import unittest
|
|
7
|
+
from unittest import mock
|
|
5
8
|
|
|
6
|
-
from engineering_process.contracts import read_json
|
|
9
|
+
from engineering_process.contracts import ProcessError, read_json
|
|
7
10
|
from engineering_process.skills import validate_skills
|
|
8
11
|
|
|
9
12
|
|
|
@@ -13,21 +16,63 @@ ROOT = Path(__file__).resolve().parent.parent
|
|
|
13
16
|
class SkillTests(unittest.TestCase):
|
|
14
17
|
def test_distribution_has_one_complete_reachable_skill_graph(self) -> None:
|
|
15
18
|
result = validate_skills(ROOT / "process_assets" / "skills", process_root=ROOT)
|
|
16
|
-
self.assertEqual("
|
|
19
|
+
self.assertEqual("deliver-change", result["entrySkill"])
|
|
17
20
|
expected = {
|
|
18
|
-
"
|
|
19
|
-
"implement
|
|
20
|
-
"improve
|
|
21
|
-
"plan
|
|
21
|
+
"change-complete",
|
|
22
|
+
"change-implement",
|
|
23
|
+
"process-improve",
|
|
24
|
+
"change-plan",
|
|
22
25
|
"production-engineering",
|
|
23
|
-
"review
|
|
24
|
-
"
|
|
25
|
-
"start
|
|
26
|
-
"verify
|
|
26
|
+
"change-review",
|
|
27
|
+
"deliver-change",
|
|
28
|
+
"change-start",
|
|
29
|
+
"change-verify",
|
|
27
30
|
}
|
|
28
31
|
self.assertEqual(expected, set(result["skills"]))
|
|
29
32
|
self.assertEqual(len(expected), result["count"])
|
|
30
33
|
|
|
34
|
+
def test_graph_rejects_missing_and_partial_transition_targets(self) -> None:
|
|
35
|
+
graph = read_json(ROOT / "process-graph.json")
|
|
36
|
+
for destination, skill in (
|
|
37
|
+
("specified", "missing-skill"),
|
|
38
|
+
("missing-state", "change-plan"),
|
|
39
|
+
(None, "change-plan"),
|
|
40
|
+
("specified", None),
|
|
41
|
+
):
|
|
42
|
+
invalid = deepcopy(graph)
|
|
43
|
+
start = next(state for state in invalid["states"] if state["id"] == "unregistered")
|
|
44
|
+
start["transitions"][0].update(nextState=destination, nextSkill=skill)
|
|
45
|
+
with self.subTest(destination=destination, skill=skill), mock.patch(
|
|
46
|
+
"engineering_process.skills.load_and_validate", return_value=invalid
|
|
47
|
+
), self.assertRaisesRegex(ProcessError, "process graph"):
|
|
48
|
+
validate_skills(ROOT / "process_assets" / "skills", process_root=ROOT)
|
|
49
|
+
|
|
50
|
+
def test_changed_source_routes_through_implementation_before_verification(self) -> None:
|
|
51
|
+
graph = read_json(ROOT / "process-graph.json")
|
|
52
|
+
for state in graph["states"]:
|
|
53
|
+
if state["id"] in {"verified", "review-pending", "approved"}:
|
|
54
|
+
transition = next(item for item in state["transitions"] if item["result"] == "source-changed")
|
|
55
|
+
with self.subTest(state=state["id"]):
|
|
56
|
+
self.assertEqual("implementing", transition["nextState"])
|
|
57
|
+
self.assertEqual("change-implement", transition["nextSkill"])
|
|
58
|
+
validate_skills(ROOT / "process_assets" / "skills", process_root=ROOT)
|
|
59
|
+
|
|
60
|
+
def test_graph_rejects_ambiguous_state_owners(self) -> None:
|
|
61
|
+
graph = read_json(ROOT / "process-graph.json")
|
|
62
|
+
graph["states"][-1]["id"] = "specified"
|
|
63
|
+
with mock.patch(
|
|
64
|
+
"engineering_process.skills.load_and_validate", return_value=graph
|
|
65
|
+
), self.assertRaisesRegex(ProcessError, "state ids must be unique"):
|
|
66
|
+
validate_skills(ROOT / "process_assets" / "skills", process_root=ROOT)
|
|
67
|
+
|
|
68
|
+
def test_readme_skill_links_resolve(self) -> None:
|
|
69
|
+
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
|
70
|
+
links = re.findall(r"\]\((process_assets/skills/[^)#]+)(?:#[^)]*)?\)", readme)
|
|
71
|
+
self.assertTrue(links)
|
|
72
|
+
for relative in links:
|
|
73
|
+
with self.subTest(relative=relative):
|
|
74
|
+
self.assertTrue((ROOT / relative).is_file())
|
|
75
|
+
|
|
31
76
|
def test_graph_exposes_only_the_six_phase_cli(self) -> None:
|
|
32
77
|
graph = read_json(ROOT / "process-graph.json")
|
|
33
78
|
commands = {
|
|
@@ -50,10 +95,10 @@ class SkillTests(unittest.TestCase):
|
|
|
50
95
|
def test_existing_skill_graph_guides_readiness_without_global_blocking(self) -> None:
|
|
51
96
|
roots = ROOT / "process_assets" / "skills"
|
|
52
97
|
required = {
|
|
53
|
-
"
|
|
54
|
-
"start
|
|
55
|
-
"plan
|
|
56
|
-
"implement
|
|
98
|
+
"deliver-change": ("processctl project validate", "never choose the\nproduct roadmap autonomously"),
|
|
99
|
+
"change-start": ("affected\nenforced capability", "Unrelated planned gaps remain visible and non-blocking"),
|
|
100
|
+
"change-plan": ("do not add unrelated planned gaps", "checklist edit alone is not evidence"),
|
|
101
|
+
"change-implement": (
|
|
57
102
|
"Never auto-promote",
|
|
58
103
|
"do not work unrelated planned\ngaps",
|
|
59
104
|
"production-engineering",
|
|
@@ -65,8 +110,8 @@ class SkillTests(unittest.TestCase):
|
|
|
65
110
|
"Approval is impossible",
|
|
66
111
|
"readiness declaration",
|
|
67
112
|
),
|
|
68
|
-
"verify
|
|
69
|
-
"review
|
|
113
|
+
"change-verify": ("do not run every planned production gate", "same snapshot"),
|
|
114
|
+
"change-review": (
|
|
70
115
|
"Do not block the change merely because unrelated planned capabilities",
|
|
71
116
|
"Priority records impact if the finding remains unresolved",
|
|
72
117
|
"never omit an\nobservation merely to reach approval",
|
|
@@ -74,14 +119,14 @@ class SkillTests(unittest.TestCase):
|
|
|
74
119
|
"bounded `processSignals`",
|
|
75
120
|
"keep the assignment `review-pending`",
|
|
76
121
|
),
|
|
77
|
-
"
|
|
122
|
+
"change-complete": (
|
|
78
123
|
"Finish never edits\nreadiness",
|
|
79
124
|
"remaining planned\ngaps",
|
|
80
125
|
"owner and stable record URL",
|
|
81
126
|
"contract-identified final consumer adoption",
|
|
82
127
|
"Closes ISSUE, closes ISSUE",
|
|
83
128
|
),
|
|
84
|
-
"improve
|
|
129
|
+
"process-improve": (
|
|
85
130
|
"new immutable pack version",
|
|
86
131
|
"pending schema-version 7 review",
|
|
87
132
|
"never\nself-publishes or self-merges",
|
|
@@ -94,7 +139,7 @@ class SkillTests(unittest.TestCase):
|
|
|
94
139
|
self.assertIn(" ".join(fragment.split()), " ".join(text.split()))
|
|
95
140
|
|
|
96
141
|
def test_improve_process_supports_owner_authorized_issue_handoff(self) -> None:
|
|
97
|
-
text = (ROOT / "process_assets" / "skills" / "improve
|
|
142
|
+
text = (ROOT / "process_assets" / "skills" / "process-improve" / "SKILL.md").read_text(encoding="utf-8")
|
|
98
143
|
for fragment in (
|
|
99
144
|
"fix or safely block the current consumer change",
|
|
100
145
|
"gh issue list --repo phuongnse/engineering-process",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_contract.py
RENAMED
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_posix.py
RENAMED
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/_supervisor_windows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/helper_launch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/publication_compat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/requirements-dev.txt
RENAMED
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process/requirements-runtime.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/requires.txt
RENAMED
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/engineering_process.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/schemas/production-engineering.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/templates/adopt-process-windows-job.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_process-1.2.6 → engineering_process-2.0.0}/tests/test_production_engineering.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|