@givolt/i3-composer-agent 3.1.0 → 3.2.1
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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude/commands/i3-sop.md +3 -3
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.gemini/commands/i3-sop.toml +3 -3
- package/README.md +22 -27
- package/commands/i3-sop.toml +3 -3
- package/docs/codex-setup.md +1 -1
- package/docs/other-hosts.md +1 -1
- package/docs/workbuddy-setup.md +1 -1
- package/evals/cases/i3-enterprise-sop.json +70 -32
- package/evals/zh-CN/cases/i3-enterprise-sop.json +77 -31
- package/package.json +1 -23
- package/plugin.json +1 -1
- package/skills/i3-enterprise-sop/SKILL.md +151 -91
- package/skills/i3-enterprise-sop/references/engineering-skeleton/directory-standard.md +29 -15
- package/skills/i3-enterprise-sop/references/handoff.py +171 -100
- package/skills/i3-enterprise-sop/references/read_text.py +60 -0
- package/skills/i3-enterprise-sop/references/technical-baseline/baselines/enterprise-application-technical-baseline-v0-0-4.md +28 -28
- package/skills/i3-enterprise-sop/references/technical-baseline/readme.md +9 -118
- package/skills/i3-enterprise-sop/references/{ad/templates → templates}/ad.md +34 -27
- package/skills/i3-enterprise-sop/references/{brd/templates → templates}/brd.md +21 -10
- package/skills/i3-enterprise-sop/references/{prd/templates → templates}/prd.md +57 -24
- package/skills/i3-enterprise-sop/references/{testcase/templates → templates}/testcase.md +32 -22
- package/docs/upstream-repository/AGENTS.md +0 -92
- package/docs/upstream-repository/CLAUDE.md +0 -60
- package/docs/upstream-repository/CONTRIBUTING.md +0 -137
- package/docs/upstream-repository/README.md +0 -8
- package/docs/upstream-repository/gitattributes.txt +0 -1
- package/docs/upstream-repository/gitignore.txt +0 -10
- package/docs/upstream-repository/manifest.json +0 -71
- package/docs/upstream-repository/opencode-skills.link +0 -1
- package/docs/upstream-repository/skill-gap.yml +0 -80
- package/docs/upstream-repository/skills-contributing.md +0 -15
- package/docs/upstream-repository/test-plugin-install.yml +0 -167
- package/skills/i3-enterprise-sop/references/ad/assets-manifest.json +0 -86
- package/skills/i3-enterprise-sop/references/ad/prompts/01-generate.md +0 -58
- package/skills/i3-enterprise-sop/references/ad/prompts/02-review.md +0 -54
- package/skills/i3-enterprise-sop/references/ad/prompts/03-revise.md +0 -54
- package/skills/i3-enterprise-sop/references/ad/prompts/04-recheck.md +0 -54
- package/skills/i3-enterprise-sop/references/ad/readme.md +0 -70
- package/skills/i3-enterprise-sop/references/assets-manifest.json +0 -230
- package/skills/i3-enterprise-sop/references/brd/assets-manifest.json +0 -83
- package/skills/i3-enterprise-sop/references/brd/prompts/01-generate.md +0 -51
- package/skills/i3-enterprise-sop/references/brd/prompts/02-review.md +0 -47
- package/skills/i3-enterprise-sop/references/brd/prompts/03-revise.md +0 -47
- package/skills/i3-enterprise-sop/references/brd/prompts/04-recheck.md +0 -47
- package/skills/i3-enterprise-sop/references/brd/readme.md +0 -50
- package/skills/i3-enterprise-sop/references/codebuddy-project-guide.md +0 -34
- package/skills/i3-enterprise-sop/references/controlled-handoff.md +0 -41
- package/skills/i3-enterprise-sop/references/controlled_handoff.py +0 -379
- package/skills/i3-enterprise-sop/references/engineering-skeleton/adoption.md +0 -200
- package/skills/i3-enterprise-sop/references/engineering-skeleton/catalog.json +0 -17
- package/skills/i3-enterprise-sop/references/engineering-skeleton/readme.md +0 -52
- package/skills/i3-enterprise-sop/references/handoff-contract.md +0 -196
- package/skills/i3-enterprise-sop/references/lifecycle-contract.md +0 -91
- package/skills/i3-enterprise-sop/references/plan-handoff.md +0 -73
- package/skills/i3-enterprise-sop/references/prd/assets-manifest.json +0 -109
- package/skills/i3-enterprise-sop/references/prd/prompts/01-generate.md +0 -64
- package/skills/i3-enterprise-sop/references/prd/prompts/02-review.md +0 -49
- package/skills/i3-enterprise-sop/references/prd/prompts/03-revise.md +0 -48
- package/skills/i3-enterprise-sop/references/prd/prompts/04-recheck.md +0 -47
- package/skills/i3-enterprise-sop/references/prd/readme.md +0 -137
- package/skills/i3-enterprise-sop/references/prd/template-contracts.md +0 -39
- package/skills/i3-enterprise-sop/references/prd/templates/ad.md +0 -17
- package/skills/i3-enterprise-sop/references/readme.md +0 -167
- package/skills/i3-enterprise-sop/references/recovery-contract.md +0 -65
- package/skills/i3-enterprise-sop/references/review-handoff.md +0 -105
- package/skills/i3-enterprise-sop/references/selection-handoff.md +0 -55
- package/skills/i3-enterprise-sop/references/selection_handoff.py +0 -225
- package/skills/i3-enterprise-sop/references/stages.json +0 -271
- package/skills/i3-enterprise-sop/references/technical-baseline/assets-manifest.json +0 -84
- package/skills/i3-enterprise-sop/references/template-contracts.md +0 -142
- package/skills/i3-enterprise-sop/references/testcase/assets-manifest.json +0 -91
- package/skills/i3-enterprise-sop/references/testcase/prompts/01-generate.md +0 -53
- package/skills/i3-enterprise-sop/references/testcase/prompts/02-review.md +0 -51
- package/skills/i3-enterprise-sop/references/testcase/prompts/03-revise.md +0 -49
- package/skills/i3-enterprise-sop/references/testcase/prompts/04-recheck.md +0 -51
- package/skills/i3-enterprise-sop/references/testcase/readme.md +0 -61
- package/skills/i3-enterprise-sop/references/verification/check_execution.py +0 -185
- package/skills/i3-enterprise-sop/references/verification/check_native_execution.py +0 -282
- package/skills/i3-enterprise-sop/references/verification/check_reads.py +0 -348
- package/skills/i3-enterprise-sop/references/verification/readme.md +0 -149
- package/skills/i3-enterprise-sop/references/verification/review-output.schema.json +0 -155
- package/skills/i3-enterprise-sop/references/verification/review_handoff.py +0 -329
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Business-document SOP handoff for one EBC per project.
|
|
3
|
+
|
|
4
|
+
Pass spec/handoff.json explicitly for new manifests; relative references use the project root.
|
|
5
|
+
Never writes documents or approvals.
|
|
6
|
+
"""
|
|
3
7
|
import argparse
|
|
4
8
|
import copy
|
|
5
9
|
import json
|
|
@@ -8,9 +12,11 @@ from pathlib import Path
|
|
|
8
12
|
import tempfile
|
|
9
13
|
import uuid
|
|
10
14
|
|
|
11
|
-
VERSION = '0.
|
|
12
|
-
GLOBAL_ROLES = {'brd'
|
|
13
|
-
TOPIC_ROLES = {'prd', 'ad', 'testCase'
|
|
15
|
+
VERSION = '0.4.0'
|
|
16
|
+
GLOBAL_ROLES = {'brd'}
|
|
17
|
+
TOPIC_ROLES = {'prd', 'ad', 'testCase'}
|
|
18
|
+
DOCUMENT_ROLES = GLOBAL_ROLES | TOPIC_ROLES
|
|
19
|
+
TOPIC_IDENTITY = ('pbcId', 'pbcName', 'prdId')
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
def _unique(pairs):
|
|
@@ -26,38 +32,93 @@ def _nonstandard(value):
|
|
|
26
32
|
raise ValueError('nonstandard JSON constant: ' + value)
|
|
27
33
|
|
|
28
34
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
def _required_text(value, label):
|
|
36
|
+
if not isinstance(value, str) or not value.strip():
|
|
37
|
+
raise ValueError(label + ' must be a nonempty string')
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _only_fields(value, allowed, label):
|
|
42
|
+
if not isinstance(value, dict):
|
|
43
|
+
raise ValueError(label + ' must be an object')
|
|
44
|
+
unexpected = set(value) - set(allowed)
|
|
45
|
+
if unexpected:
|
|
46
|
+
raise ValueError('unrecognized ' + label + ' fields; preserve the original and resolve their purpose: '
|
|
47
|
+
+ ', '.join(sorted(unexpected)))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _validate(value):
|
|
51
|
+
if not isinstance(value, dict) or type(value.get('schemaVersion')) is not int:
|
|
52
|
+
raise ValueError('unsupported handoff schemaVersion; expected integer 2')
|
|
53
|
+
if value['schemaVersion'] == 1:
|
|
54
|
+
raise ValueError('legacy handoff schemaVersion 1; preserve the original, verify EBC/PBC identities '
|
|
55
|
+
'and document references before an explicitly requested update; no automatic migration')
|
|
56
|
+
if value['schemaVersion'] != 2:
|
|
57
|
+
raise ValueError('unsupported handoff schemaVersion; expected 2; no automatic migration')
|
|
58
|
+
_only_fields(value, {'schemaVersion', 'scope', 'selected'}, 'handoff')
|
|
33
59
|
scope = value.get('scope')
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
_only_fields(scope, {'ebcId', 'ebcName'}, 'scope')
|
|
61
|
+
for key in ('ebcId', 'ebcName'):
|
|
62
|
+
_required_text(scope.get(key), 'scope.' + key)
|
|
63
|
+
selected = value.get('selected')
|
|
64
|
+
_only_fields(selected, GLOBAL_ROLES | {'topics'}, 'selected')
|
|
65
|
+
for role in GLOBAL_ROLES & set(selected):
|
|
66
|
+
_required_text(selected[role], 'selected.' + role)
|
|
67
|
+
topics = selected.get('topics')
|
|
39
68
|
if not isinstance(topics, list):
|
|
40
69
|
raise ValueError('selected.topics must be an array')
|
|
41
70
|
seen = set()
|
|
71
|
+
names = {}
|
|
42
72
|
for topic in topics:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
73
|
+
_only_fields(topic, set(TOPIC_IDENTITY) | TOPIC_ROLES, 'topic')
|
|
74
|
+
for key in TOPIC_IDENTITY:
|
|
75
|
+
_required_text(topic.get(key), 'topic.' + key)
|
|
76
|
+
identity = (topic['pbcId'], topic['prdId'])
|
|
46
77
|
if identity in seen:
|
|
47
78
|
raise ValueError('ambiguous duplicate topic selection')
|
|
48
79
|
seen.add(identity)
|
|
80
|
+
if names.setdefault(topic['pbcId'], topic['pbcName']) != topic['pbcName']:
|
|
81
|
+
raise ValueError('conflicting pbcName for pbcId: ' + topic['pbcId'])
|
|
82
|
+
roles = TOPIC_ROLES & set(topic)
|
|
83
|
+
if not roles:
|
|
84
|
+
raise ValueError('topic must contain at least one selected PRD, AD or TestCase path')
|
|
85
|
+
for role in roles:
|
|
86
|
+
_required_text(topic[role], 'topic.' + role)
|
|
49
87
|
return value
|
|
50
88
|
|
|
51
89
|
|
|
90
|
+
def _load(raw):
|
|
91
|
+
return _validate(json.loads(raw, object_pairs_hook=_unique, parse_constant=_nonstandard))
|
|
92
|
+
|
|
93
|
+
|
|
52
94
|
def _selected_only(value):
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
if unexpected:
|
|
56
|
-
raise ValueError('unrecognized top-level fields; preserve the original and resolve their purpose before writing: '
|
|
57
|
-
+ ', '.join(sorted(unexpected)))
|
|
95
|
+
"""Validate the complete v2 record before any canonical write; never discard fields."""
|
|
96
|
+
_validate(value)
|
|
58
97
|
return {key: copy.deepcopy(value[key]) for key in ('schemaVersion', 'scope', 'selected')}
|
|
59
98
|
|
|
60
99
|
|
|
100
|
+
def _check_scope(value, ebc_id, ebc_name):
|
|
101
|
+
if value['scope']['ebcId'] != ebc_id:
|
|
102
|
+
raise ValueError('manifest belongs to a different EBC scope')
|
|
103
|
+
if ebc_name is not None and _required_text(ebc_name, 'ebcName') != value['scope']['ebcName']:
|
|
104
|
+
raise ValueError('ebcName conflicts with the selected EBC; verify the document identity before updating')
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _topic_name(selected, pbc_id, prd_id, pbc_name):
|
|
108
|
+
_required_text(pbc_id, 'pbcId')
|
|
109
|
+
_required_text(prd_id, 'prdId')
|
|
110
|
+
topics = selected['topics']
|
|
111
|
+
matches = [x for x in topics if x['pbcId'] == pbc_id and x['prdId'] == prd_id]
|
|
112
|
+
if pbc_name is None:
|
|
113
|
+
if not matches:
|
|
114
|
+
raise ValueError('new topic requires an explicit pbcName')
|
|
115
|
+
pbc_name = matches[0]['pbcName']
|
|
116
|
+
_required_text(pbc_name, 'pbcName')
|
|
117
|
+
if any(x['pbcId'] == pbc_id and x['pbcName'] != pbc_name for x in topics):
|
|
118
|
+
raise ValueError('pbcName conflicts with the selected PBC; verify the document identity before updating')
|
|
119
|
+
return pbc_name
|
|
120
|
+
|
|
121
|
+
|
|
61
122
|
def _manifest_path(root, raw):
|
|
62
123
|
if not raw:
|
|
63
124
|
raise ValueError('an explicit manifest path is required for this operation')
|
|
@@ -75,8 +136,8 @@ def _manifest_path(root, raw):
|
|
|
75
136
|
return path
|
|
76
137
|
|
|
77
138
|
|
|
78
|
-
def _reference(root, role, raw,
|
|
79
|
-
if role not in
|
|
139
|
+
def _reference(root, role, raw, pbc_id=None, prd_id=None, pbc_name=None):
|
|
140
|
+
if role not in DOCUMENT_ROLES:
|
|
80
141
|
raise ValueError('unknown reference role')
|
|
81
142
|
if not isinstance(raw, str) or not raw.strip():
|
|
82
143
|
raise ValueError('reference path must be explicit')
|
|
@@ -84,86 +145,85 @@ def _reference(root, role, raw, capability_id=None, prd_id=None):
|
|
|
84
145
|
resolved = (path if path.is_absolute() else root / path).resolve()
|
|
85
146
|
if not path.is_absolute() and not resolved.is_relative_to(root):
|
|
86
147
|
raise ValueError('relative reference escapes project root; use an explicitly adopted absolute source')
|
|
87
|
-
if resolved.is_file():
|
|
88
|
-
if not resolved.read_text(encoding='utf-8').strip():
|
|
89
|
-
raise ValueError('empty document is not a complete handoff reference')
|
|
90
|
-
elif role != 'engineeringSkeleton' or not resolved.is_dir():
|
|
148
|
+
if not resolved.is_file():
|
|
91
149
|
raise ValueError('reference missing or unreadable: ' + raw)
|
|
92
|
-
|
|
150
|
+
if not resolved.read_text(encoding='utf-8').strip():
|
|
151
|
+
raise ValueError('empty document is not a complete handoff reference')
|
|
152
|
+
result = {'role': role,
|
|
153
|
+
'path': resolved.relative_to(root).as_posix() if resolved.is_relative_to(root) else str(resolved)}
|
|
93
154
|
if role in TOPIC_ROLES:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
result.update(capabilityId=capability_id, prdId=prd_id)
|
|
155
|
+
for key, item in [('pbcId', pbc_id), ('pbcName', pbc_name), ('prdId', prd_id)]:
|
|
156
|
+
result[key] = _required_text(item, key)
|
|
97
157
|
return result
|
|
98
158
|
|
|
99
159
|
|
|
100
|
-
def _assign(selected, reference, *, transient=False):
|
|
160
|
+
def _assign(selected, reference, *, transient=False, replace_document=False):
|
|
101
161
|
role, path = reference['role'], reference['path']
|
|
102
162
|
target = selected
|
|
103
163
|
if role in TOPIC_ROLES:
|
|
104
|
-
topics = selected
|
|
105
|
-
|
|
164
|
+
topics = selected['topics']
|
|
165
|
+
_topic_name(selected, reference['pbcId'], reference['prdId'], reference['pbcName'])
|
|
166
|
+
matches = [x for x in topics if x['pbcId'] == reference['pbcId'] and x['prdId'] == reference['prdId']]
|
|
106
167
|
if len(matches) > 1:
|
|
107
168
|
raise ValueError('ambiguous duplicate topic selection')
|
|
108
169
|
if matches:
|
|
109
170
|
target = matches[0]
|
|
110
171
|
else:
|
|
111
|
-
target = {k: reference[k] for k in
|
|
172
|
+
target = {k: reference[k] for k in TOPIC_IDENTITY}
|
|
112
173
|
topics.append(target)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
else:
|
|
122
|
-
if not transient and role in target and target[role] != path:
|
|
123
|
-
raise ValueError('existing selection differs; replacement and affected-pair recovery belong to S08')
|
|
124
|
-
target[role] = path
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def _resolve(root, value, capability_id, prd_id, manual):
|
|
174
|
+
previous_path = target.get(role)
|
|
175
|
+
if not transient and previous_path is not None and previous_path != path and not replace_document:
|
|
176
|
+
raise ValueError('updating a selected document requires --ebc-id matching scope.ebcId')
|
|
177
|
+
target[role] = path
|
|
178
|
+
return previous_path
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _resolve(root, value, pbc_id, prd_id, pbc_name, manual):
|
|
128
182
|
selected = copy.deepcopy(value['selected'])
|
|
129
|
-
topics = selected
|
|
130
|
-
if
|
|
131
|
-
raise ValueError('provide both
|
|
132
|
-
if
|
|
133
|
-
|
|
183
|
+
topics = selected['topics']
|
|
184
|
+
if (pbc_id is None) != (prd_id is None):
|
|
185
|
+
raise ValueError('provide both pbcId and prdId')
|
|
186
|
+
if pbc_id is not None:
|
|
187
|
+
_required_text(pbc_id, 'pbcId')
|
|
188
|
+
_required_text(prd_id, 'prdId')
|
|
189
|
+
elif len(topics) == 1:
|
|
190
|
+
pbc_id, prd_id = topics[0]['pbcId'], topics[0]['prdId']
|
|
191
|
+
if pbc_name is not None:
|
|
192
|
+
_topic_name(selected, pbc_id, prd_id, pbc_name)
|
|
134
193
|
manual_roles = set()
|
|
135
194
|
for item in manual:
|
|
136
195
|
role, separator, path = item.partition('=')
|
|
137
196
|
if not separator or role in manual_roles:
|
|
138
197
|
raise ValueError('manual references require one unambiguous ROLE=PATH per role')
|
|
139
198
|
manual_roles.add(role)
|
|
140
|
-
|
|
199
|
+
name = _topic_name(selected, pbc_id, prd_id, pbc_name) if role in TOPIC_ROLES else None
|
|
200
|
+
reference = _reference(root, role, path, pbc_id, prd_id, name)
|
|
141
201
|
_assign(selected, reference, transient=True)
|
|
142
|
-
topics = selected
|
|
143
|
-
if
|
|
144
|
-
raise ValueError('multiple topics; explicitly choose
|
|
202
|
+
topics = selected['topics']
|
|
203
|
+
if pbc_id is None and len(topics) > 1:
|
|
204
|
+
raise ValueError('multiple topics; explicitly choose pbcId and prdId')
|
|
145
205
|
references = []
|
|
146
206
|
for role in sorted(GLOBAL_ROLES):
|
|
147
207
|
if role in selected:
|
|
148
208
|
references.append(_reference(root, role, selected[role]))
|
|
149
|
-
matches = [x for x in topics if x['
|
|
150
|
-
if
|
|
209
|
+
matches = [x for x in topics if x['pbcId'] == pbc_id and x['prdId'] == prd_id]
|
|
210
|
+
if pbc_id is not None and not matches:
|
|
151
211
|
raise ValueError('requested topic is not selected; no highest-version fallback')
|
|
152
212
|
for topic in matches:
|
|
153
213
|
for role in sorted(TOPIC_ROLES):
|
|
154
214
|
if role not in topic:
|
|
155
215
|
continue
|
|
156
|
-
|
|
157
|
-
if not isinstance(paths, list):
|
|
158
|
-
raise ValueError('contracts must be a path array')
|
|
159
|
-
for path in paths:
|
|
160
|
-
references.append(_reference(root, role, path, capability_id, prd_id))
|
|
216
|
+
references.append(_reference(root, role, topic[role], pbc_id, prd_id, topic['pbcName']))
|
|
161
217
|
for reference in references:
|
|
162
218
|
path = Path(reference['path'])
|
|
163
219
|
reference['resolvedPath'] = str(path if path.is_absolute() else root / path)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
220
|
+
result = {'ok': True, 'changed': False, 'source': 'manual' if manual else 'manifest',
|
|
221
|
+
'scope': copy.deepcopy(value['scope']), 'references': references,
|
|
222
|
+
'proof': 'document paths read now; caller must verify document identities, versions, upstream '
|
|
223
|
+
'and adopted attachments against the declared EBC/PBC scope and actual human decisions'}
|
|
224
|
+
if matches:
|
|
225
|
+
result['topic'] = {key: matches[0][key] for key in TOPIC_IDENTITY}
|
|
226
|
+
return result
|
|
167
227
|
|
|
168
228
|
|
|
169
229
|
def _store(root, path, old, value):
|
|
@@ -209,63 +269,74 @@ def _store(root, path, old, value):
|
|
|
209
269
|
lock.unlink()
|
|
210
270
|
|
|
211
271
|
|
|
212
|
-
def execute(project_root, manifest, action, *,
|
|
213
|
-
|
|
272
|
+
def execute(project_root, manifest, action, *, ebc_id=None, ebc_name=None, role=None, path=None,
|
|
273
|
+
pbc_id=None, pbc_name=None, prd_id=None, manual=()):
|
|
214
274
|
root = Path(project_root).resolve()
|
|
215
275
|
if not root.is_dir():
|
|
216
276
|
raise ValueError('explicit project root must already exist')
|
|
277
|
+
_required_text(ebc_id, 'ebcId')
|
|
278
|
+
if action not in {'init', 'select', 'resolve'}:
|
|
279
|
+
raise ValueError('unknown handoff action')
|
|
280
|
+
if action != 'resolve' and manual:
|
|
281
|
+
raise ValueError('manual overrides are read-only resolve inputs')
|
|
217
282
|
if action == 'resolve':
|
|
218
283
|
if manifest:
|
|
219
284
|
target = _manifest_path(root, manifest)
|
|
220
285
|
if not target.is_file():
|
|
221
286
|
raise ValueError('manifest missing; no automatic creation in read-only resolve')
|
|
222
287
|
value = _load(target.read_bytes())
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
288
|
+
_check_scope(value, ebc_id, ebc_name)
|
|
289
|
+
elif manual:
|
|
290
|
+
value = {'schemaVersion': 2,
|
|
291
|
+
'scope': {'ebcId': ebc_id, 'ebcName': _required_text(ebc_name, 'ebcName')},
|
|
292
|
+
'selected': {'topics': []}}
|
|
227
293
|
else:
|
|
228
|
-
raise ValueError('resolve requires an explicit manifest or manual references with
|
|
229
|
-
return _resolve(root, value,
|
|
294
|
+
raise ValueError('resolve requires an explicit manifest or manual references with EBC identity')
|
|
295
|
+
return _resolve(root, value, pbc_id, prd_id, pbc_name, manual)
|
|
230
296
|
target = _manifest_path(root, manifest)
|
|
231
297
|
old = target.read_bytes() if target.is_file() else None
|
|
232
298
|
if action == 'init':
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
raise ValueError('existing manifest belongs to a different work scope')
|
|
299
|
+
_required_text(ebc_name, 'ebcName')
|
|
300
|
+
value = _load(old) if old is not None else {
|
|
301
|
+
'schemaVersion': 2, 'scope': {'ebcId': ebc_id, 'ebcName': ebc_name}, 'selected': {'topics': []}}
|
|
302
|
+
_check_scope(value, ebc_id, ebc_name)
|
|
238
303
|
return _store(root, target, old, value)
|
|
239
304
|
if action == 'select':
|
|
240
|
-
if manual:
|
|
241
|
-
raise ValueError('manual overrides are read-only resolve inputs')
|
|
242
305
|
if old is None:
|
|
243
|
-
raise ValueError('manifest missing; initialize the explicit
|
|
306
|
+
raise ValueError('manifest missing; initialize the explicit EBC first or use manual resolve')
|
|
244
307
|
value = _load(old)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
308
|
+
_check_scope(value, ebc_id, ebc_name)
|
|
309
|
+
if role in GLOBAL_ROLES and any(item is not None for item in (pbc_id, pbc_name, prd_id)):
|
|
310
|
+
raise ValueError('BRD selection belongs to the EBC and does not take PBC or PRD identity')
|
|
311
|
+
name = _topic_name(value['selected'], pbc_id, prd_id, pbc_name) if role in TOPIC_ROLES else None
|
|
312
|
+
reference = _reference(root, role, path, pbc_id, prd_id, name)
|
|
313
|
+
previous_path = _assign(value['selected'], reference, replace_document=True)
|
|
314
|
+
result = _store(root, target, old, value)
|
|
315
|
+
if previous_path is not None and previous_path != reference['path']:
|
|
316
|
+
result['updatedSelection'] = dict(reference, previousPath=previous_path)
|
|
317
|
+
return result
|
|
251
318
|
|
|
252
319
|
|
|
253
320
|
def main():
|
|
254
321
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
255
322
|
parser.add_argument('action', choices=['init', 'select', 'resolve'])
|
|
256
|
-
parser.add_argument('--project-root', required=True)
|
|
257
|
-
parser.add_argument('--manifest'
|
|
258
|
-
|
|
259
|
-
parser.add_argument('--
|
|
260
|
-
parser.add_argument('--
|
|
261
|
-
parser.add_argument('--
|
|
323
|
+
parser.add_argument('--project-root', required=True, help='Explicit business project root; one EBC per project')
|
|
324
|
+
parser.add_argument('--manifest', help='Explicit manifest path; new projects use spec/handoff.json; '
|
|
325
|
+
'omit only for resolve with manual inputs')
|
|
326
|
+
parser.add_argument('--ebc-id', required=True, help='Expected EBC identity; checked on every operation')
|
|
327
|
+
parser.add_argument('--ebc-name', help='Required for init and manual resolve without a manifest')
|
|
328
|
+
parser.add_argument('--role', choices=sorted(DOCUMENT_ROLES))
|
|
329
|
+
parser.add_argument('--path', help='Explicit saved document path; new BRD: spec/<EBC-name>-BRD_v<version>.md; '
|
|
330
|
+
'new PRD/AD/TestCase: spec/<PBC-name>/...; relative to the business project root')
|
|
331
|
+
parser.add_argument('--pbc-id')
|
|
332
|
+
parser.add_argument('--pbc-name', help='Required when creating a topic; existing names must match')
|
|
262
333
|
parser.add_argument('--prd-id')
|
|
263
334
|
parser.add_argument('--manual', action='append', default=[], metavar='ROLE=PATH')
|
|
264
335
|
args = parser.parse_args()
|
|
265
336
|
try:
|
|
266
|
-
result = execute(args.project_root, args.manifest, args.action,
|
|
267
|
-
|
|
268
|
-
prd_id=args.prd_id, manual=args.manual)
|
|
337
|
+
result = execute(args.project_root, args.manifest, args.action, ebc_id=args.ebc_id,
|
|
338
|
+
ebc_name=args.ebc_name, role=args.role, path=args.path,
|
|
339
|
+
pbc_id=args.pbc_id, pbc_name=args.pbc_name, prd_id=args.prd_id, manual=args.manual)
|
|
269
340
|
except (OSError, ValueError, UnicodeError) as error:
|
|
270
341
|
print(json.dumps({'ok': False, 'error': str(error)}, ensure_ascii=False))
|
|
271
342
|
return 1
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Return one bounded UTF-8 text page; use nextOffset unchanged for the next page."""
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def render(path, text, start, end):
|
|
9
|
+
return json.dumps({'path': str(path), 'offset': start, 'nextOffset': end,
|
|
10
|
+
'eof': end == len(text), 'text': text[start:end]},
|
|
11
|
+
ensure_ascii=False, separators=(',', ':'))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_page(path, offset=0, max_chars=6000):
|
|
15
|
+
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
|
16
|
+
raise ValueError('offset must be a nonnegative integer')
|
|
17
|
+
if not isinstance(max_chars, int) or isinstance(max_chars, bool) or not 512 <= max_chars <= 10000:
|
|
18
|
+
raise ValueError('max-chars must be an integer from 512 to 10000')
|
|
19
|
+
source = Path(path).resolve(strict=True)
|
|
20
|
+
text = source.read_bytes().decode('utf-8')
|
|
21
|
+
if not text.strip():
|
|
22
|
+
raise ValueError('selected text file is empty')
|
|
23
|
+
if offset > len(text):
|
|
24
|
+
raise ValueError('offset is beyond the selected text; use the preceding nextOffset')
|
|
25
|
+
low, high = offset, min(len(text), offset + max_chars)
|
|
26
|
+
if len(render(source, text, offset, offset)) + 1 > max_chars:
|
|
27
|
+
raise ValueError('max-chars is too small for the path metadata')
|
|
28
|
+
while low < high:
|
|
29
|
+
end = (low + high + 1) // 2
|
|
30
|
+
if len(render(source, text, offset, end)) + 1 <= max_chars:
|
|
31
|
+
low = end
|
|
32
|
+
else:
|
|
33
|
+
high = end - 1
|
|
34
|
+
end = low
|
|
35
|
+
if end == offset and offset < len(text):
|
|
36
|
+
raise ValueError('max-chars leaves no room for text')
|
|
37
|
+
if end < len(text):
|
|
38
|
+
newline = text.rfind('\n', offset, end)
|
|
39
|
+
if newline >= offset:
|
|
40
|
+
end = newline + 1
|
|
41
|
+
return render(source, text, offset, end)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main():
|
|
45
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
46
|
+
parser.add_argument('--path', required=True)
|
|
47
|
+
parser.add_argument('--offset', type=int, default=0, help='Character cursor returned by the preceding page; initial value 0')
|
|
48
|
+
parser.add_argument('--max-chars', type=int, default=6000, help='Maximum serialized page length, including metadata and trailing newline')
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
try:
|
|
51
|
+
page = read_page(args.path, args.offset, args.max_chars)
|
|
52
|
+
except (OSError, ValueError, UnicodeError) as error:
|
|
53
|
+
print(json.dumps({'ok': False, 'error': str(error)}, ensure_ascii=False))
|
|
54
|
+
return 1
|
|
55
|
+
print(page)
|
|
56
|
+
return 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == '__main__':
|
|
60
|
+
raise SystemExit(main())
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
|
|
13
13
|
| 项目 | 当前内容 |
|
|
14
14
|
| --- | --- |
|
|
15
|
-
| 文档版本 | v0.0.4,2026-10-22
|
|
16
|
-
| 文档性质 |
|
|
17
|
-
| 适用范围 |
|
|
18
|
-
| 项目绑定 | 本文件不绑定某一业务项目;项目名称、采用版本、PRD/AD
|
|
15
|
+
| 文档版本 | v0.0.4,2026-10-22;当前分发规约,工程目录规范基线为1.0.0 |
|
|
16
|
+
| 文档性质 | 根据使用者逐项确认整理的企业共用技术基线;作为 i3-enterprise-sop 生成 AD的默认技术输入,项目明确规约优先;新稿仍可人工审阅修改 |
|
|
17
|
+
| 适用范围 | 使用本 Skill默认规约或明确采用本文件的企业业务项目及其前端、业务后端、Agent、任务工程;各工程按实际需求采用 |
|
|
18
|
+
| 项目绑定 | 本文件不绑定某一业务项目;项目名称、采用版本、PRD/AD与实际工程在AD来源与采用表中记录,见 Q01、Q12 |
|
|
19
19
|
| 直接来源 | 软件架构设计 Specification 模板 v0.6(2026-09-23),含原文明确的技术方向及工程约束 |
|
|
20
20
|
| 编排依据 | 当前企业 SOP 的公共技术规约模板、AD 模板及交接合同;工程组织引用现有目录规范1.0.0 |
|
|
21
21
|
| 确认依据 | 2026-10-20确认原有共用规则;2026-10-21确认将推荐版本矩阵补入规约,K8s暂按既有1.28记录。确认技术选择不等于授权升级、安装、部署或通过兼容验收 |
|
|
22
22
|
| 维护与采用 | 共用规则及选型方向按上述确认维护;具体项目仍需记录采用版本、适用范围、责任和例外,不由本轮确认替代业务认可或开工许可 |
|
|
23
23
|
| 现有证据 | 已核官方版本/依赖要求及本机工具;已完成Dapr 1.18.7针对K8s 1.28的三组离线Helm渲染。版本矩阵已明确;尚无目标集群连接、工程安装/构建及完整联调证明,见§2.3—§2.6 |
|
|
24
24
|
|
|
25
|
-
本文件定位为可复用的企业共用技术基线,已写入确认的选型方向与共用约定。第8
|
|
25
|
+
本文件定位为可复用的企业共用技术基线,已写入确认的选型方向与共用约定。第8节分别记录“已经确认的规则”和“项目仍需落实的事实”,不能把版本、环境或运行结果自动标成已验证。生成AD默认带入本文件时,在AD准确记录其路径、文内版本及适用条款;项目另有明确规约则优先沿用;按影响起草和交接,不要求所有项目事实先清零。
|
|
26
26
|
|
|
27
|
-
本文件是业务项目的技术资料,不是 i3-composer-agent
|
|
27
|
+
本文件是业务项目的技术资料,不是 i3-composer-agent 工具自身的运行规范。显式使用 i3-enterprise-sop生成AD时默认带入,单独安装或标准流程不自动启用;项目已有明确规范时优先沿用,避免建立第二份相互冲突的规约。
|
|
28
28
|
|
|
29
29
|
### 0.1 来源与整理边界
|
|
30
30
|
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
| 同一来源,第 12—17 章及附录 | 工程质量、环境、流水线、实施交接、验证与未决事项 | 本文件第 6—9 节 |
|
|
36
36
|
| 使用者本轮逐项确认(2026-10-20) | 共用基线定位、Java/Spring Boot、前端工具链、MaxKey/OIDC、接口惯例、目录采用、部署分工及按项目落实的规则 | 对应第2—9节;不扩展为版本兼容、业务批准或实际环境就绪 |
|
|
37
37
|
| 2026-10-21官方资料核对、本机只读检查与使用者确认 | 固定首套版本选择、保留K8s 1.28及现有GitLab;记录兼容范围与真实验证边界 | 本文件§2.3—§2.6及Q02—Q06、Q09;不自动执行升级或替项目采用 |
|
|
38
|
-
| [
|
|
39
|
-
| [当前 AD 模板](../../
|
|
40
|
-
| [
|
|
41
|
-
| [目录规范1.0.0](../../engineering-skeleton/directory-standard.md)
|
|
42
|
-
|
|
|
38
|
+
| [公共技术规约资料](../readme.md) | 已有规范优先、公共规则与主题设计分工、候选与真实采用分开 | 本文件的范围、采用和版本规则 |
|
|
39
|
+
| [当前 AD 模板](../../templates/ad.md) §0.3、§2.2、§9.6 | 基线引用、技术落实和真实骨架来源 | 各主题 AD 的对应条款 |
|
|
40
|
+
| [本 Skill 工程交接说明](../../../SKILL.md) 中的 T2/T3 | 选定 AD及必要配套形成工程 SPEC,再交原 Plan及获准 Build;原命令等效规格方式保留 | 本文件第9节及本 Skill T2/T3;具体技术规则未改 |
|
|
41
|
+
| [目录规范1.0.0](../../engineering-skeleton/directory-standard.md) | 工程目录与职责、AD映射、原Plan任务路径、获准Build的实际文件落点 | 本文件第1、6—9节;目录规范可先采用,不要求预先取得代码框架模板 |
|
|
42
|
+
| 原Ship命令及原Spec的内容要求(通过实际宿主发现;[调用边界](../../../SKILL.md)) | 发布前检查/决策/回滚,及原SPEC六项的工程命令和项目依据 | 本文件第6—7、9节;原命令与Skill未改 |
|
|
43
43
|
|
|
44
44
|
源材料沿用总体 `design.md` 和旧 MVP 产物组织。本文件只提取其中适用的技术内容,并按当前已确认的“公共规约+多主题 PRD/AD”组织;不恢复旧 MVP 流程,不改动源文件,不要求把公共规则复制到每份 AD。原文要求明确写出规则的目的继续保留:AD 必须指出准确规则、适用范围和落实位置,不能只写“沿用旧规范”。
|
|
45
45
|
|
|
46
|
-
本规约保留已确认技术条款及其来源说明,仅分发当前版本;工程组织统一采用同一SOP Skill内的1.0.0
|
|
46
|
+
本规约保留已确认技术条款及其来源说明,仅分发当前版本;工程组织统一采用同一SOP Skill内的1.0.0目录规范,采用信息由AD现有表格记录。来源材料或发行资源后续修改,不自动改变项目已经采用的版本。
|
|
47
47
|
|
|
48
48
|
## 1. 共用架构与工程边界
|
|
49
49
|
|
|
@@ -136,7 +136,7 @@ Java若实际采用Spring集成,候选坐标为`io.dapr.spring:dapr-spring-boo
|
|
|
136
136
|
| GitLab Runner | 19.2.x,实际补丁待核 | 与GitLab主/次版本匹配;未取得实例信息前不编造精确补丁号或可用Runner |
|
|
137
137
|
| Docker Desktop | 本机4.70.0 | 4.96.0作为另行评估的更新候选;本轮未启动Docker或升级。CLI版本不证明实际运行的Engine版本 |
|
|
138
138
|
| Helm及集群部署工具 | 项目实际工具待选择 | 研究中临时使用的Helm 3.19.0不是项目默认工具,也不构成本机全局安装事实 |
|
|
139
|
-
| 工程骨架 | 统一采用本SOP Skill
|
|
139
|
+
| 工程骨架 | 统一采用本SOP Skill内的目录规范1.0.0 | 按本次确认统一来源;项目既有采用不自动升级,先核差异、适用范围及真实工程映射。目录规则不等于可运行代码模板 |
|
|
140
140
|
|
|
141
141
|
### 2.4 已取得的环境事实与K8s兼容边界
|
|
142
142
|
|
|
@@ -251,7 +251,7 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
251
251
|
|
|
252
252
|
### 6.1 工程骨架与文件责任
|
|
253
253
|
|
|
254
|
-
- ENG-01:工程骨架沿用现有[目录规范1.0.0](../../engineering-skeleton/directory-standard.md)
|
|
254
|
+
- ENG-01:工程骨架沿用现有[目录规范1.0.0](../../engineering-skeleton/directory-standard.md)。`pbc-composite`与`agent-standalone`是组织方式,frontend、backend、agents、jobs及配套契约、共享代码、测试和交付位置按需采用;内部遵循实际语言/框架惯例。
|
|
255
255
|
- ENG-02:目录规范是实际可读的设计输入,可以在工程创建前采用。AD记录规范来源/版本、业务项目根、已有/拟建工程和模块映射;未采用代码框架模板时如实说明,不要求预先取得企业代码模板或冻结首套完整技术栈。
|
|
256
256
|
- ENG-03:AD §9.2明确源码、模块、配置、测试、契约及装配位置,§9.6说明已有工程与拟建内容;原Plan把预计新增/修改文件路径、依赖和验证写入任务;获准的Build在这些位置生成实际内容,核对文件落点并执行原有测试/构建。
|
|
257
257
|
- ENG-04:共享底座、契约和文件只有一处维护责任及实际任务来源,其他主题引用依赖;新增 PRD 不自动复制底座。再次生成或更新时保留人工修改、历史及已完成任务,冲突先核范围。
|
|
@@ -261,7 +261,7 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
261
261
|
|
|
262
262
|
既有工程先映射实际目录,不为套规范移动或重建。实际有内容时再建所需位置;项目根的构建/CI配置与框架原有布局允许按实情保留。目录检查、代码存在、构建、启动、业务验证和发布分别取证。
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
来源:现有工程骨架目录方案、目录规范1.0.0,以及当前AD §9.2/§9.6的对应栏目。本轮只引用这些资源,不修改工程骨架工作树或SOP运行模板;原命令的读取与实际落点继续按对应工程验证任务取证。
|
|
265
265
|
|
|
266
266
|
### 6.2 工程命令与编码规范
|
|
267
267
|
|
|
@@ -312,14 +312,14 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
312
312
|
|
|
313
313
|
| 编号 | 已确认的共用处理 | 项目仍需落实的内容与影响 | 处理时点 |
|
|
314
314
|
| --- | --- | --- | --- |
|
|
315
|
-
| Q01 | 本文作为可复用企业技术基线,项目明确采用准确版本 |
|
|
315
|
+
| Q01 | 本文作为可复用企业技术基线,项目明确采用准确版本 | 项目名称、维护责任、范围和既有规范由AD现有来源与采用表绑定;不在公共规约预造业务身份 | 实际项目采用时 |
|
|
316
316
|
| Q02 | 首套普通业务后端采用Java+Spring Boot方向,与Python Agent分工 | §2.3已明确JDK/Boot/Maven/JDBC目标;核实际工程依赖、企业框架复用、启动及序列化,不将版本确认写为兼容已通过 | 相关详设和工程任务按依赖核实 |
|
|
317
317
|
| Q03 | 保留Vue 3+TypeScript+Antdv Next,工具链采用Vite、类型检查和Vitest方向 | §2.3已明确首套前端版本;核实际依赖/锁文件,按需引入路由/状态管理,安装/类型/构建/测试结果仍待验证 | 初始化及依赖安装前核实,生成后验证 |
|
|
318
318
|
| Q04 | Dapr/Agent按实际需要的能力建立组合版本和验证安排 | 按§2.3固定所用版本;在保留的K8s 1.28上验证实际安装、必要业务链、中断恢复、重复与授权失效,离线渲染不替代运行证据 | 依赖相关能力的设计/工程阶段分别核实 |
|
|
319
319
|
| Q05 | PostgreSQL优先兼容企业已有受支持版本,逻辑责任与恢复分别设计 | 先定位已有服务端;17.11仅是无既有约束的新建候选,驱动42.7.14已定,扩展/实例/迁移/DDL/恢复仍须真实证据 | 数据工程设计、迁移及运行前 |
|
|
320
320
|
| Q06 | 统一身份采用MaxKey,通过OIDC接入;业务服务执行最终授权 | 4.1.12为首轮接入基线,投产前核4.2.0安全更新;取得实际实例/类型、应用注册、映射、退出撤权及配置引用,不虚构claims或可用能力 | 身份相关契约和实现前 |
|
|
321
321
|
| Q07 | API camelCase、数据库snake_case、跨端大整数ID字符串、明确时间点时区、稳定业务码与关联标识 | 正式契约补精确字段、分页上限、格式/精度、主键生成、审计及存量映射,避免擅改已有契约 | 相关公共契约及主题AD定稿前 |
|
|
322
|
-
| Q08 |
|
|
322
|
+
| Q08 | 引用现有目录规范1.0.0,沿用原AD→Plan→Build的目录映射链 | 明确业务根及真实/拟建路径;目录规范可先采用,实际代码由获准Build生成验证;只有另行要求特定代码模板才须取得该模板 | AD映射、Plan任务文件路径、Build实际落点依次核对 |
|
|
323
323
|
| Q09 | AD规定部署要求,Plan安排任务,Build形成配置,Ship核验汇总部署准备 | K8s 1.28和GitLab 19.2.4为使用者提供的环境;补完整集群信息、Runner及实际资源/权限/配置,部署资料不冒充平台已就绪 | 构建、联调、部署和发布按需落实 |
|
|
324
324
|
| Q10 | 保留已有质量下限,新增指标从PRD或真实工程决定取得 | 质量、容量、恢复、预算、保留、告警与适用要求的具体值和测量方法属于项目;不得填造通用数字 | 对应测试设计、处理数据或放行前 |
|
|
325
325
|
| Q11 | 根据实际情况填写各工程命令、cwd、前提、成功依据和风格 | 已有工程读取真实配置;新工程按任务创建并验证,未验证保持实际状态;不要求用户预先写假命令 | 本次规划与任务执行按依赖补足 |
|
|
@@ -333,18 +333,18 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
333
333
|
|
|
334
334
|
### 9.1 进入 AD
|
|
335
335
|
|
|
336
|
-
1.
|
|
336
|
+
1. 使用 i3-enterprise-sop生成AD时,项目未另有明确规约就默认读取本规约和目录规范,在AD记录路径、版本及范围;已有明确规约优先,冲突先说明。
|
|
337
337
|
2. 选定准确 PRD 主题和版本。起草/修订 AD 必须按企业入口加载原 Spec Skill,使用完整 AD 模板;本公共规约本身不替代主题规格。
|
|
338
|
-
3. AD §0.3
|
|
338
|
+
3. AD §0.3登记本规约及目录规范的来源和版本,§2.2落实技术选择,§9.2映射工程及模块位置,§9.6区分现有工程和拟建内容;目录规范可在代码生成前采用,保留三图和全部详设责任。
|
|
339
339
|
4. 原型按需,可与 AD 并行。记录假设和受影响未决;候选生成、完整回读和人工认可分别处理,保存后遵守原 Spec 停轮。
|
|
340
340
|
|
|
341
|
-
|
|
341
|
+
以下是明确委托本 Skill生成AD的任务示例;默认资料从本Skill直接定位,项目明确规约优先,不要求用户填写安装内部路径:
|
|
342
342
|
|
|
343
343
|
```text
|
|
344
|
-
|
|
345
|
-
|
|
344
|
+
使用 i3-enterprise-sop 的 ad 动作。
|
|
345
|
+
将自带《企业应用公共技术规约 v0.0.4》和目录规范作为默认输入;项目已有明确规约时优先沿用。
|
|
346
346
|
先确定当前业务项目、我已选定的 PRD 主题和准确版本;存在歧义先问。
|
|
347
|
-
|
|
347
|
+
实际读取这份规约、其引用的工程目录规范1.0.0及本次必要来源,列出适用条款、已有规范冲突和影响本主题的项目缺口。
|
|
348
348
|
按原 Spec Skill 和完整 AD 模板生成本主题设计,保留三图、工程细节和 SPEC 六项。
|
|
349
349
|
沿用§2.3已确认的版本矩阵,保留既有K8s 1.28与GitLab 19.2.4;逐项区分目标与实装,未验证的兼容性、具体字段和工程映射按实际来源补足。
|
|
350
350
|
按现有目录规范明确本主题源码、模块、配置、测试和契约的已有/拟建位置,不要求预先提供一套代码框架模板,也不宣称工程已生成。
|
|
@@ -353,14 +353,14 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
353
353
|
|
|
354
354
|
### 9.2 交原 Plan
|
|
355
355
|
|
|
356
|
-
以选定AD为入口,读取其准确PRD
|
|
356
|
+
以选定AD为入口,读取其准确PRD、本规约适用条款、共享契约、目录规范及AD工程映射和已有代码;尚无代码时如实记录拟建,不虚构工程根。按主题加载完整必要内容,核本范围SPEC六项和FR/AC;Plan任务保留真实/拟建文件路径及验证依据,不全量注入无关AD和历史。
|
|
357
357
|
|
|
358
|
-
|
|
358
|
+
本规约的来源标识、准确路径或URL、文内版本或固定提交、适用条款、采用范围与例外、实际读取及缺口,统一记录在AD §0.3;工程映射与实际模板/骨架来源由AD §9.6承接。后续工程SPEC/Plan沿AD引用读取准确原件,不再将规约路径或采用信息写入 `handoff.json`,也不另建采用说明。`handoff.json`仅记录EBC/PBC身份及已选BRD/PRD/AD/TestCase文档路径,主题AD仍按相应文档角色登记。可以手动给出准确路径,不强制创建清单;引用或登记不证明已读,也不表示文档获批。
|
|
359
359
|
|
|
360
360
|
```text
|
|
361
361
|
/i3-plan
|
|
362
|
-
以我本轮明确选定的AD为规划入口,实际读取它引用的PRD
|
|
363
|
-
|
|
362
|
+
以我本轮明确选定的AD为规划入口,实际读取它引用的PRD、公共技术规约适用条款、共享契约、工程目录规范及已有代码。
|
|
363
|
+
公共技术规约按AD §0.3采用记录中的准确路径、文内版本及适用条款回读,不以AD引用或清单存在代替正文读取;规约采用信息不写入handoff.json。
|
|
364
364
|
核对本主题需求、验收条件、SPEC 六项、共享依赖和未决影响;TestCase 按需引用。
|
|
365
365
|
按AD的工程/模块映射,将每项任务预计新增或修改的文件路径、真实来源、初始化或增量内容及验证依据保留在原计划和唯一任务目标中,交我审阅,暂不实施。
|
|
366
366
|
```
|
|
@@ -373,7 +373,7 @@ PostgreSQL已定位的是Homebrew libpq 18.3工具。在已查常见路径、进
|
|
|
373
373
|
|
|
374
374
|
### 9.4 本次交付结论
|
|
375
375
|
|
|
376
|
-
本版保留2026-10-20/21确认的共用规则、技术版本矩阵、本机及既有平台事实、官方依据与V01—V06验证安排,并按2026-10-22
|
|
376
|
+
本版保留2026-10-20/21确认的共用规则、技术版本矩阵、本机及既有平台事实、官方依据与V01—V06验证安排,并按2026-10-22确认采用工程目录规范1.0.0,本副本的采用信息直接由AD记录。Q02—Q06、Q09区分已选择版本与仍待取得的环境/运行证据;其他已确认规则保留。K8s 1.28继续保留,三组Dapr离线渲染不作为真实集群通过证明。
|
|
377
377
|
|
|
378
378
|
本文件可供人工审阅,并可在明确选用后作为 AD 的公共输入。它不证明某项目的整体技术设计已经批准,也不证明所有主题可交 Plan、可直接 Build 或已通过运行验收。
|
|
379
379
|
|