@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.
Files changed (86) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude/commands/i3-sop.md +3 -3
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/.claude-plugin/plugin.json +1 -1
  5. package/.codex-plugin/plugin.json +1 -1
  6. package/.gemini/commands/i3-sop.toml +3 -3
  7. package/README.md +22 -27
  8. package/commands/i3-sop.toml +3 -3
  9. package/docs/codex-setup.md +1 -1
  10. package/docs/other-hosts.md +1 -1
  11. package/docs/workbuddy-setup.md +1 -1
  12. package/evals/cases/i3-enterprise-sop.json +70 -32
  13. package/evals/zh-CN/cases/i3-enterprise-sop.json +77 -31
  14. package/package.json +1 -23
  15. package/plugin.json +1 -1
  16. package/skills/i3-enterprise-sop/SKILL.md +151 -91
  17. package/skills/i3-enterprise-sop/references/engineering-skeleton/directory-standard.md +29 -15
  18. package/skills/i3-enterprise-sop/references/handoff.py +171 -100
  19. package/skills/i3-enterprise-sop/references/read_text.py +60 -0
  20. package/skills/i3-enterprise-sop/references/technical-baseline/baselines/enterprise-application-technical-baseline-v0-0-4.md +28 -28
  21. package/skills/i3-enterprise-sop/references/technical-baseline/readme.md +9 -118
  22. package/skills/i3-enterprise-sop/references/{ad/templates → templates}/ad.md +34 -27
  23. package/skills/i3-enterprise-sop/references/{brd/templates → templates}/brd.md +21 -10
  24. package/skills/i3-enterprise-sop/references/{prd/templates → templates}/prd.md +57 -24
  25. package/skills/i3-enterprise-sop/references/{testcase/templates → templates}/testcase.md +32 -22
  26. package/docs/upstream-repository/AGENTS.md +0 -92
  27. package/docs/upstream-repository/CLAUDE.md +0 -60
  28. package/docs/upstream-repository/CONTRIBUTING.md +0 -137
  29. package/docs/upstream-repository/README.md +0 -8
  30. package/docs/upstream-repository/gitattributes.txt +0 -1
  31. package/docs/upstream-repository/gitignore.txt +0 -10
  32. package/docs/upstream-repository/manifest.json +0 -71
  33. package/docs/upstream-repository/opencode-skills.link +0 -1
  34. package/docs/upstream-repository/skill-gap.yml +0 -80
  35. package/docs/upstream-repository/skills-contributing.md +0 -15
  36. package/docs/upstream-repository/test-plugin-install.yml +0 -167
  37. package/skills/i3-enterprise-sop/references/ad/assets-manifest.json +0 -86
  38. package/skills/i3-enterprise-sop/references/ad/prompts/01-generate.md +0 -58
  39. package/skills/i3-enterprise-sop/references/ad/prompts/02-review.md +0 -54
  40. package/skills/i3-enterprise-sop/references/ad/prompts/03-revise.md +0 -54
  41. package/skills/i3-enterprise-sop/references/ad/prompts/04-recheck.md +0 -54
  42. package/skills/i3-enterprise-sop/references/ad/readme.md +0 -70
  43. package/skills/i3-enterprise-sop/references/assets-manifest.json +0 -230
  44. package/skills/i3-enterprise-sop/references/brd/assets-manifest.json +0 -83
  45. package/skills/i3-enterprise-sop/references/brd/prompts/01-generate.md +0 -51
  46. package/skills/i3-enterprise-sop/references/brd/prompts/02-review.md +0 -47
  47. package/skills/i3-enterprise-sop/references/brd/prompts/03-revise.md +0 -47
  48. package/skills/i3-enterprise-sop/references/brd/prompts/04-recheck.md +0 -47
  49. package/skills/i3-enterprise-sop/references/brd/readme.md +0 -50
  50. package/skills/i3-enterprise-sop/references/codebuddy-project-guide.md +0 -34
  51. package/skills/i3-enterprise-sop/references/controlled-handoff.md +0 -41
  52. package/skills/i3-enterprise-sop/references/controlled_handoff.py +0 -379
  53. package/skills/i3-enterprise-sop/references/engineering-skeleton/adoption.md +0 -200
  54. package/skills/i3-enterprise-sop/references/engineering-skeleton/catalog.json +0 -17
  55. package/skills/i3-enterprise-sop/references/engineering-skeleton/readme.md +0 -52
  56. package/skills/i3-enterprise-sop/references/handoff-contract.md +0 -196
  57. package/skills/i3-enterprise-sop/references/lifecycle-contract.md +0 -91
  58. package/skills/i3-enterprise-sop/references/plan-handoff.md +0 -73
  59. package/skills/i3-enterprise-sop/references/prd/assets-manifest.json +0 -109
  60. package/skills/i3-enterprise-sop/references/prd/prompts/01-generate.md +0 -64
  61. package/skills/i3-enterprise-sop/references/prd/prompts/02-review.md +0 -49
  62. package/skills/i3-enterprise-sop/references/prd/prompts/03-revise.md +0 -48
  63. package/skills/i3-enterprise-sop/references/prd/prompts/04-recheck.md +0 -47
  64. package/skills/i3-enterprise-sop/references/prd/readme.md +0 -137
  65. package/skills/i3-enterprise-sop/references/prd/template-contracts.md +0 -39
  66. package/skills/i3-enterprise-sop/references/prd/templates/ad.md +0 -17
  67. package/skills/i3-enterprise-sop/references/readme.md +0 -167
  68. package/skills/i3-enterprise-sop/references/recovery-contract.md +0 -65
  69. package/skills/i3-enterprise-sop/references/review-handoff.md +0 -105
  70. package/skills/i3-enterprise-sop/references/selection-handoff.md +0 -55
  71. package/skills/i3-enterprise-sop/references/selection_handoff.py +0 -225
  72. package/skills/i3-enterprise-sop/references/stages.json +0 -271
  73. package/skills/i3-enterprise-sop/references/technical-baseline/assets-manifest.json +0 -84
  74. package/skills/i3-enterprise-sop/references/template-contracts.md +0 -142
  75. package/skills/i3-enterprise-sop/references/testcase/assets-manifest.json +0 -91
  76. package/skills/i3-enterprise-sop/references/testcase/prompts/01-generate.md +0 -53
  77. package/skills/i3-enterprise-sop/references/testcase/prompts/02-review.md +0 -51
  78. package/skills/i3-enterprise-sop/references/testcase/prompts/03-revise.md +0 -49
  79. package/skills/i3-enterprise-sop/references/testcase/prompts/04-recheck.md +0 -51
  80. package/skills/i3-enterprise-sop/references/testcase/readme.md +0 -61
  81. package/skills/i3-enterprise-sop/references/verification/check_execution.py +0 -185
  82. package/skills/i3-enterprise-sop/references/verification/check_native_execution.py +0 -282
  83. package/skills/i3-enterprise-sop/references/verification/check_reads.py +0 -348
  84. package/skills/i3-enterprise-sop/references/verification/readme.md +0 -149
  85. package/skills/i3-enterprise-sop/references/verification/review-output.schema.json +0 -155
  86. package/skills/i3-enterprise-sop/references/verification/review_handoff.py +0 -329
@@ -1,348 +0,0 @@
1
- """Read-only checker for CodeBuddy stream-json evidence. No project or report writes."""
2
- import json,re
3
- from pathlib import Path
4
-
5
- def assess(project_root, scope, events):
6
- root=Path(project_root).resolve()
7
- def relative(raw):
8
- p=Path(raw);p=(p if p.is_absolute() else root/p).resolve()
9
- return str(p.relative_to(root)) if p.is_relative_to(root) else str(p)
10
- calls=[];results={};finals=[]
11
- for seq,event in enumerate(events):
12
- if event.get('type')=='result':finals.append(event)
13
- message=event.get('message',{})
14
- if not isinstance(message,dict):continue
15
- for block in message.get('content',[]):
16
- if not isinstance(block,dict):continue
17
- if event.get('type')=='assistant' and block.get('type')=='tool_use':
18
- calls.append({**block,'event':seq})
19
- if event.get('type')=='user' and block.get('type')=='tool_result':
20
- results[block['tool_use_id']]={**block,'receiptEvent':seq}
21
- reads=[];writes=[];errors=[]
22
- for call in calls:
23
- inp=call.get('input',{});result=results.get(call['id'])
24
- raw=inp.get('file_path') or inp.get('path')
25
- path=relative(raw) if raw else None
26
- success=result is not None and not result.get('is_error',False)
27
- item={'tool':call['name'],'path':path,'event':call['event'],
28
- 'receiptEvent':result['receiptEvent'] if result else float('inf'),'successful':success}
29
- if call['name']=='Read':
30
- data=result.get('content',[]) if result else []
31
- text=data if isinstance(data,str) else '\n'.join(x.get('text','') for x in data if isinstance(x,dict))
32
- reads.append({**item,'lines':[int(n) for n in re.findall(r'^\s*(\d+)→',text,re.M)]})
33
- elif call['name'] in ['Write','Edit','Bash','PowerShell','NotebookEdit','Skill','Task','Agent']:
34
- writes.append(item)
35
- if result and result.get('is_error'):errors.append(item)
36
- allowed={relative(p) for p in scope.get('allowedWrites',[])}
37
- first_write=min((x['event'] for x in writes if x['successful']),default=float('inf'))
38
- coverage=[]
39
- for raw in scope['requiredReads']:
40
- path=relative(raw);p=root/path
41
- count=scope.get('beforeLineCounts',{}).get(raw)
42
- if count is None:count=len(p.read_text().splitlines()) if p.is_file() else None
43
- observed=[x for x in reads if x['path']==path and x['successful'] and x['receiptEvent']<first_write]
44
- lines={n for x in observed for n in x['lines']}
45
- full=count is not None and bool(observed) and all(n in lines for n in range(1,count+1))
46
- coverage.append({'path':path,'exists':p.is_file(),'expectedLines':count,'successfulRead':bool(observed),'fullRead':full})
47
- missing=[x['path'] for x in coverage if not x['successfulRead']]
48
- partial=[x['path'] for x in coverage if x['successfulRead'] and not x['fullRead']]
49
- explicit_external={relative(p) for p in scope['requiredReads'] if Path(relative(p)).is_absolute()}
50
- forbidden=[]
51
- for read in reads:
52
- p=read['path']
53
- if read['successful'] and ((Path(p).is_absolute() and p not in explicit_external) or any(p==relative(x) or p.startswith(relative(x).rstrip('/')+'/') for x in scope.get('forbiddenAccessPrefixes',[]))):forbidden.append(p)
54
- targets=[]
55
- for path in sorted(allowed):
56
- p=root/path;changes=[x for x in writes if x['path']==path and x['successful'] and x['tool'] in ['Write','Edit']]
57
- last=max((x['receiptEvent'] for x in changes),default=float('inf'))
58
- final_reads=[x for x in reads if x['path']==path and x['successful'] and x['event']>last and x['receiptEvent']>last]
59
- count=len(p.read_text().splitlines()) if p.is_file() else None
60
- lines={n for x in final_reads for n in x['lines']}
61
- full=bool(changes and final_reads) and count is not None and all(n in lines for n in range(1,count+1))
62
- targets.append({'path':path,'lastWriteEvent':last if changes else None,'finalLines':count,'fullReadAfterLastEdit':full})
63
- failures=[]
64
- if not finals or finals[-1].get('is_error',False):failures.append('native_final_not_proven')
65
- if missing:failures.append('required_read_missing')
66
- if partial:failures.append('required_read_incomplete')
67
- if forbidden:failures.append('forbidden_source_read')
68
- if any(x['tool'] not in ['Write','Edit'] or x['path'] not in allowed for x in writes):failures.append('tool_or_write_boundary')
69
- if any(not x['fullReadAfterLastEdit'] for x in targets):failures.append('final_full_read_incomplete')
70
- return {'format':'i3-read-evidence/v1','passed':not failures,'failures':failures,'requiredReadCoverage':coverage,
71
- 'missingReads':missing,'partialReads':partial,'forbiddenReads':sorted(set(forbidden)),'targetChecks':targets,
72
- 'observedReads':reads,'observedWrites':writes,'toolErrors':errors,
73
- 'proofScope':'actual native reads and final read coverage only; not semantic correctness, approval or runtime interception'}
74
-
75
- def _schema_errors(value, node, schema, location='$'):
76
- """Validate only the keywords used by our bundled contract, without dependencies.
77
-
78
- This is not a general JSON Schema engine. Unknown keywords fail closed.
79
- """
80
- supported = {'$schema', '$id', 'title', 'description', 'type', 'required', 'properties',
81
- 'additionalProperties', 'items', 'minItems', 'minimum', 'minLength',
82
- 'enum', '$ref', '$defs'}
83
- if set(node) - supported:
84
- raise ValueError('unsupported bundled schema keyword at ' + location)
85
- if '$ref' in node:
86
- prefix = '#/$defs/'
87
- ref = node['$ref']
88
- if not ref.startswith(prefix):
89
- raise ValueError('only bundled local schema references are supported')
90
- return _schema_errors(value, schema['$defs'][ref[len(prefix):]], schema, location)
91
- types = {'object': lambda x: isinstance(x, dict), 'array': lambda x: isinstance(x, list),
92
- 'string': lambda x: isinstance(x, str), 'integer': lambda x: type(x) is int}
93
- kind = node.get('type')
94
- if kind and (kind not in types or not types[kind](value)):
95
- return [location + ': expected ' + str(kind)]
96
- errors = []
97
- if 'enum' in node and value not in node['enum']:
98
- errors.append(location + ': value outside enum')
99
- if kind == 'object':
100
- properties = node.get('properties', {})
101
- errors += [location + '.' + key + ': missing' for key in node.get('required', []) if key not in value]
102
- if node.get('additionalProperties') is False:
103
- errors += [location + '.' + key + ': unknown field' for key in value if key not in properties]
104
- for key in value.keys() & properties.keys():
105
- errors += _schema_errors(value[key], properties[key], schema, location + '.' + key)
106
- elif kind == 'array':
107
- if len(value) < node.get('minItems', 0):
108
- errors.append(location + ': too few items')
109
- for index, item in enumerate(value):
110
- errors += _schema_errors(item, node.get('items', {}), schema, f'{location}[{index}]')
111
- elif kind == 'string' and len(value.strip()) < node.get('minLength', 0):
112
- errors.append(location + ': empty string')
113
- elif kind == 'integer' and value < node.get('minimum', value):
114
- errors.append(location + ': below minimum')
115
- return errors
116
-
117
-
118
- def _source_role_bindings(scope):
119
- """One caller-owned role table for both schema generation and evidence checks."""
120
- if not isinstance(scope, dict):
121
- raise ValueError('review scope must be an object')
122
- roles = scope.get('sourceRoles', {})
123
- required = scope.get('requiredSourceRoles', [])
124
- if (not isinstance(roles, dict)
125
- or not all(isinstance(k, str) and isinstance(v, str) and k.strip() and v.strip() for k, v in roles.items())
126
- or not isinstance(required, list)
127
- or any(not isinstance(k, str) or k not in roles for k in required)):
128
- raise ValueError('sourceRoles/requiredSourceRoles must be explicit caller bindings')
129
- return dict(roles), list(required)
130
-
131
-
132
- def build_review_schema(scope):
133
- """Build a fresh CLI-compatible job schema from the bundled schema and scope.
134
-
135
- Role enums are bound before generation. No project, scope or report writes.
136
- The evidence validator uses the same _source_role_bindings function.
137
- """
138
- roles, _ = _source_role_bindings(scope)
139
- schema = json.loads(Path(__file__).with_name('review-output.schema.json').read_text())
140
- properties = schema['$defs']['reference']['properties']
141
- if roles:
142
- properties['role']['enum'] = sorted(roles)
143
- else:
144
- properties.pop('role', None)
145
-
146
- def inline(node):
147
- if isinstance(node, list):
148
- return [inline(x) for x in node]
149
- if not isinstance(node, dict):
150
- return node
151
- if '$ref' in node:
152
- prefix = '#/$defs/'
153
- if not node['$ref'].startswith(prefix):
154
- raise ValueError('only bundled local schema references are supported')
155
- return inline(schema['$defs'][node['$ref'][len(prefix):]])
156
- return {k: inline(v) for k, v in node.items() if k not in ['$defs', '$schema', '$id']}
157
- return inline(schema)
158
-
159
-
160
- def _unique_object(pairs):
161
- result = {}
162
- for key, value in pairs:
163
- if key in result:
164
- raise ValueError('duplicate JSON key: ' + key)
165
- result[key] = value
166
- return result
167
-
168
-
169
- def _invalid_constant(value):
170
- raise ValueError('nonstandard JSON constant: ' + value)
171
-
172
-
173
- def check_review(project_root, events, assessment, scope=None):
174
- scope = scope or {}
175
- issues, checked = [], []
176
- def fail(code, detail):
177
- issues.append({'code': code, 'detail': detail})
178
- def result(**extra):
179
- return {'passed': not issues, 'errors': [x['detail'] for x in issues],
180
- 'errorCodes': sorted({x['code'] for x in issues}), 'issues': issues,
181
- 'quotes': checked, 'contract': 'i3-enterprise-sop-review-output/v1', **extra}
182
- finals = [x for x in events if x.get('type') == 'result']
183
- text = finals[-1].get('result', '') if finals else ''
184
- carrier = scope.get('reviewCarrier', 'text')
185
- if carrier not in ['text', 'structured_output']:
186
- fail('review_carrier_invalid', 'unsupported explicitly selected review carrier')
187
- return result()
188
- if carrier == 'structured_output':
189
- if not finals or 'structured_output' not in finals[-1]:
190
- fail('review_output_missing', 'requested native structured_output missing; no text fallback')
191
- return result()
192
- payload = finals[-1]['structured_output']
193
- calls, successful = {}, set()
194
- for event in events:
195
- message = event.get('message', {})
196
- content = message.get('content', []) if isinstance(message, dict) else []
197
- if not isinstance(content, list):
198
- continue
199
- for block in content:
200
- if not isinstance(block, dict):
201
- continue
202
- if block.get('type') == 'tool_use' and block.get('name') == 'StructuredOutput':
203
- calls[block.get('id')] = block.get('input')
204
- elif block.get('type') == 'tool_result' and not block.get('is_error', False):
205
- successful.add(block.get('tool_use_id'))
206
- if not any(tool_id in successful and value == payload for tool_id, value in calls.items()):
207
- fail('review_structured_output_unproven', 'no matching successful native StructuredOutput tool/result pair')
208
- return result()
209
- try:
210
- raw = json.dumps(payload, ensure_ascii=False, allow_nan=False)
211
- except (ValueError, TypeError) as error:
212
- fail('review_json_invalid', str(error))
213
- return result()
214
- else:
215
- if not isinstance(text, str) or not text.strip():
216
- fail('review_output_missing', 'native final report text missing')
217
- return result()
218
- blocks = re.findall(r'```json[ \t]*\r?\n(.*?)\r?\n```', text, re.S)
219
- if len(blocks) > 1:
220
- fail('review_json_ambiguous', 'expected one JSON object, found multiple JSON blocks')
221
- return result()
222
- raw = blocks[0] if blocks else text.strip()
223
- try:
224
- report = json.loads(raw, object_pairs_hook=_unique_object, parse_constant=_invalid_constant)
225
- except (ValueError, TypeError) as error:
226
- fail('review_json_invalid', str(error))
227
- return result(parseError={'message': str(error), 'line': getattr(error, 'lineno', None),
228
- 'column': getattr(error, 'colno', None), 'position': getattr(error, 'pos', None)})
229
- checked_report = check_report(project_root, report, assessment, scope)
230
- checked_report['carrier'] = carrier
231
- checked_report['nativeCarrierProven'] = carrier == 'structured_output'
232
- return checked_report
233
-
234
-
235
- def check_report(project_root, report, assessment, scope=None):
236
- """Check report content against source/read evidence; no native-origin claim.
237
-
238
- The caller must record the origin of a separately corrected report. Calling
239
- this function does not turn that report into a new native model result.
240
- """
241
- root = Path(project_root).resolve()
242
- scope = scope or {}
243
- carrier = 'report-object'
244
- issues, checked = [], []
245
- def fail(code, detail):
246
- issues.append({'code': code, 'detail': detail})
247
- def result(**extra):
248
- return {'passed': not issues, 'errors': [x['detail'] for x in issues],
249
- 'errorCodes': sorted({x['code'] for x in issues}), 'issues': issues,
250
- 'quotes': checked, 'contract': 'i3-enterprise-sop-review-output/v1', **extra}
251
- try:
252
- schema = json.loads(Path(__file__).with_name('review-output.schema.json').read_text())
253
- schema_errors = _schema_errors(report, schema, schema)
254
- except (ValueError, KeyError, OSError) as error:
255
- fail('review_contract_invalid', str(error))
256
- return result()
257
- if not schema_errors:
258
- for group in ['findings', 'checkedFacts']:
259
- ids = [x['id'] for x in report[group]]
260
- if len(ids) != len(set(ids)):
261
- schema_errors.append(group + ': duplicate stable ID')
262
- if schema_errors:
263
- for error in schema_errors:
264
- fail('review_schema_invalid', error)
265
- return result()
266
-
267
- def relative(raw_path):
268
- p = Path(raw_path)
269
- p = (p if p.is_absolute() else root / p).resolve()
270
- return str(p.relative_to(root)) if p.is_relative_to(root) else str(p)
271
- try:
272
- roles, required_roles = _source_role_bindings(scope)
273
- except ValueError as error:
274
- fail('review_role_scope_invalid', str(error))
275
- return result()
276
- roles = {name: relative(path) for name, path in roles.items()}
277
- read_lines = {}
278
- for item in assessment['observedReads']:
279
- if item['successful']:
280
- read_lines.setdefault(item['path'], set()).update(item['lines'])
281
- proven_roles = set()
282
- for group in ['findings', 'checkedFacts']:
283
- for index, item in enumerate(report[group]):
284
- for side in ['target', 'source']:
285
- ref = item[side]
286
- label = f'{group}[{index}].{side}'
287
- path = relative(ref['path'])
288
- start, end = ref['startLine'], ref.get('endLine', ref['startLine'])
289
- bound_role = ref.get('role')
290
- role_matches = bound_role is None
291
- if bound_role is not None:
292
- if bound_role not in roles:
293
- fail('review_source_role_unbound', label + ': unbound role ' + bound_role)
294
- elif roles[bound_role] != path:
295
- fail('review_source_role_mismatch', label + ': role does not match caller-bound path')
296
- else:
297
- role_matches = True
298
- observed = start <= end and all(n in read_lines.get(path, set()) for n in range(start, end + 1))
299
- quote_matches = False
300
- if start > end:
301
- fail('review_reference_range_invalid', label + ': endLine precedes startLine')
302
- else:
303
- # Do not open an arbitrary external path merely because the model cites it.
304
- p = root / path
305
- explicit_external = {relative(x) for x in scope.get('requiredReads', [])}
306
- permitted = p.is_relative_to(root) or path in explicit_external
307
- try:
308
- lines = p.read_text().splitlines() if permitted and p.is_file() else []
309
- quote_matches = end <= len(lines) and ref['quote'] in '\n'.join(lines[start-1:end])
310
- except (OSError, UnicodeError):
311
- pass
312
- if not quote_matches:
313
- fail('review_quote_mismatch', label + ': quote does not match referenced current file/lines')
314
- if not observed:
315
- fail('review_reference_not_read', label + ': cited lines lack successful native Read evidence')
316
- if bound_role and role_matches and observed and quote_matches:
317
- proven_roles.add(bound_role)
318
- checked.append({'group': group, 'index': index, 'role': side, 'path': path,
319
- 'sourceRole': bound_role, 'quoteMatches': quote_matches,
320
- 'citedLinesActuallyRead': observed, 'sourceRoleMatches': role_matches})
321
- for name in required_roles:
322
- if name not in proven_roles:
323
- fail('review_source_role_missing', 'required role lacks proven reference: ' + name)
324
- return result(verdict=report['verdict'], carrier=carrier, sourceRoleEvidence=sorted(proven_roles),
325
- scope='structure, exact quotes, actual Read ranges and caller-bound roles only; not semantic correctness or approval')
326
-
327
- def main():
328
- import argparse,sys
329
- parser=argparse.ArgumentParser(description=__doc__)
330
- parser.add_argument('--project-root',required=True)
331
- parser.add_argument('--scope',required=True,help='Verification scope only; not business handoff.json')
332
- parser.add_argument('--trace',required=True,help='CodeBuddy stream-json trace')
333
- parser.add_argument('--review-quotes',action='store_true')
334
- args=parser.parse_args()
335
- try:
336
- scope=json.loads(Path(args.scope).read_text())
337
- events=[json.loads(line) for line in Path(args.trace).read_text().splitlines() if line.strip()]
338
- if not isinstance(scope.get('requiredReads'),list):raise ValueError('scope.requiredReads must be a list')
339
- result=assess(args.project_root,scope,events)
340
- if args.review_quotes:
341
- quote_check=check_review(args.project_root,events,result,scope);result['reviewQuoteCheck']=quote_check
342
- if not quote_check['passed']:result['failures'].extend(quote_check['errorCodes']);result['passed']=False
343
- except (ValueError,KeyError,TypeError,OSError,UnicodeError) as error:
344
- result={'format':'i3-read-evidence/v1','passed':False,'failures':['invalid_or_unsupported_evidence'],'detail':str(error)}
345
- print(json.dumps(result,ensure_ascii=False,indent=2))
346
- return 0 if result['passed'] else 1
347
-
348
- if __name__=='__main__':raise SystemExit(main())
@@ -1,149 +0,0 @@
1
- # 外层读取与审查证据校验
2
-
3
- <!-- i3:zh-guide:start -->
4
- ## 中文使用说明
5
-
6
- 按下文完整工程步骤、检查项与验证条件执行。根据任务选择适用步骤,保留证据、风险和退出条件;正文示例中的英文标签在面向用户输出时译为中文。
7
-
8
- 默认使用简体中文解释、追问及生成报告;用户明确要求其他语言时可切换。原方法正文、引用、阈值和审批条件完整保留。代码标识、路径、命令、工具参数、JSON 键/枚举与原文引用保持原样。
9
- <!-- i3:zh-guide:end -->
10
-
11
- ## 独立文档审查接点
12
-
13
- review/recheck 使用[统一审查约定](../review-handoff.md)承接语义与简明输出规则;本说明和Schema由外层解析/保护,不要求模型为校验再重复全文读取。只对固定覆盖组合适用,未知自改仍完整读取选定材料;本阶段README、模板、业务与实际约束不得裁剪。其他校验配置和下文历史使用记录保持原意。
14
-
15
- [review_handoff.py](review_handoff.py)按[独立只读审查合同](../review-handoff.md)组织一次显式review/recheck:从所选索引定位完整模板和唯一动作,创建新的CodeBuddy session,仅提供Read/Glob/Grep及报告输出,关闭其他Agent、写入工具、Shell与外部MCP。外层复用本文check_reads.py的Schema、读取与引用校验,另核session、精确目标/来源、复查旧ID及项目和输入快照。报告仅保存在项目外,原生输出不改,业务稿、三字段JSON、Plan和人工内容零写入。缺宿主时交task.md给新会话;这不是跨宿主通用适配、OS只读沙箱或普通会话自动守卫。辅助的具体支持范围以本轮真实证据为准,不能把已有工具说明当宿主验收。
16
-
17
- ## 显式受控入口与回执时序
18
-
19
- [controlled_handoff.py](../controlled_handoff.py) 是单次 CodeBuddy 只读预检到明确选用登记的显式适配,使用[受控交接合同](../controlled-handoff.md)。它先运行只读原生会话,复用本文 native 校验器;全部通过且已有本次准确文档的明确选用委托后,外层固定参数调用原 handoff.py,写后核实际差量。下列只读证据工具本身仍不执行登记或自动拦截。
20
-
21
- 同主题改选复用同一原生读取/报告校验;具体见[改选合同](../selection-handoff.md)。预览证据在项目外,确认后新的只读会话重新核来源;[selection_handoff.py](../selection_handoff.py)只负责准确组合判据、影响预览和复用 handoff._store,不修改业务稿或Plan。缺授权、配套未知、预览失效或漏读时零替换;实际写入尝试、结果与回读另存 selection-registration.json/selection-readback.json,不假称模型执行过 shell。checkbox 和路径命中仅是预览线索,不是已执行或语义兼容证明。
22
-
23
- native 校验器 v0.1.1 与 check_reads.py 的写前读取统一按成功 tool_result 到达时间;方法加载亦核成功回执。写后全文回读必须在最后成功写入回执后重新发起,不能拿并行发起的旧读冒充。旧原始报告保留,不追认旧作业通过;读取证据不等于理解或业务批准。
24
-
25
- ## 原生方法与执行报告
26
-
27
- 只读工具[check_native_execution.py](check_native_execution.py) v0.1.1,面向CodeBuddy stream-json的generation、status、plan、auto-preflight和standard验证配置。它不替换下面的check_reads.py或S06专用check_execution.py,不自动启用、运行命令、修改项目或业务handoff.json。
28
-
29
- ```bash
30
- python3 <资料根>/verification/check_native_execution.py --project-root <唯一项目根> --scope <调用前冻结的验证范围.json> --trace <原始轨迹.jsonl> --process <进程退出记录.json>
31
- ```
32
-
33
- scope由外层准备,不要求业务用户手填:profile;requiredReads;reportInputs;allowedWrites;files(工具inventory在调用前取得的文件/模式快照);requiredSkills;tools;allowedShellCommands(准确只读命令);gateExpectations。可另列forbiddenReads、forbiddenAccessPrefixes和forbiddenSkills。安装定位元数据的selected-line例外只接受requiredReadLines中的.codebuddy/.i3-composer-agent.json,不用于裁剪业务正文。
34
-
35
- 读取、原方法加载、最终目标回读、项目边界、工具纪律、报告一致性和门禁表述分别输出,不因提前停止而自动清除requiredSkills或未读项。原Build入口要求的两个Skill仍分别核成功回执及本地完整正文;“缺规格时停住”不等于完整命令调用通过。安全行为与业务语义由外层结合原文另审,不能由总体passed替代。
36
-
37
- 全部真实调用、顺序、ID、回执和轨迹行由工具提取;is_error=false不覆盖明确Error文本,Bash须有成功退出码。白名单事先覆盖同一项目必要的直接git status和git -C形式,不借复测临时放开任意Bash;文件发现使用获准Glob/Grep。拒绝保留,不通过旁路或重复调用追绿。
38
-
39
- 调用者将本次reportInputs、gateExpectations和下面的格式随任务提供。最终会话附且仅附一个有界JSON块;不同时套用S06 shellAttempts块。failedAttempts按原始顺序列全部非成功尝试;tool是实际工具名,subject为Skill名、完整Bash命令或项目相对文件路径,status只用blocked/failed/unconfirmed。无失败填空数组。inputChecks列外层指定路径,status只用full/partial/not_read,按实际全文读取证据填写。gates列外层指定门禁,status只用not_applicable/not_checked/pending/blocked/satisfied;不是让模型创造批准。不得增加role或另抄全量成功调用计数。
40
-
41
- <!-- i3-sop-native-execution-report -->
42
- ```json
43
- {"schemaVersion":1,"failedAttempts":[],"inputChecks":[{"path":"CODEBUDDY.md","status":"full"}],"gates":[{"id":"plan-approval","status":"pending"}]}
44
- ```
45
- <!-- /i3-sop-native-execution-report -->
46
-
47
- gateExpectations是验证用例的待核表述,不是授权凭证。匹配只证明报告没有擅改调用前条件;satisfied不产生真人认可。检查器校验结构化输入声明,并检查有限的“无工具/无Bash”矛盾文字;其余自由文字如“配套均已核”、版本、未知消费者和实际门禁充分性仍须只读审查。历史轨迹用--legacy-report单独输出not_checked_legacy,不追溯新格式、改原模型回答或追认旧失败为通过。自然语言空白差异与内容缺失分开判断,机器载荷逐字节透传另测。
48
-
49
- 读取/审查工具check_reads.py版本:v0.3.1。只读工具,适配CodeBuddy stream-json,不新增Skill、命令、Hook、业务JSON字段或自动守卫。由外层调用者执行,结果输出stdout,退出码0表示本次证据检查通过,1表示证据不完整或格式不支持;工具不创建报告文件。仅用Python标准库,该审查输出结构的唯一契约为[review-output.schema.json](review-output.schema.json)。内置检查器只实现该契约使用的关键字,未知关键字报错,不承诺校验任意JSON Schema。执行/报告复核使用本文末尾独立工具和[交接合同§9](../handoff-contract.md#9-真实调用与执行报告),不套用业务审查的role与引用Schema。
50
-
51
- ## 执行/报告定向复核
52
-
53
- [check_execution.py](check_execution.py) v0.1.0只读分析CodeBuddy的Bash tool_use/tool_result,针对明确的单项目handoff.py直接调用。真实工具尝试不会因description写“占位”而消失;权限拒绝也列入。此工具不注入宿主、不自动运行、不代替完整读取/业务审查,也不负责拦截命令。
54
-
55
- ```bash
56
- python3 <资源根>/verification/check_execution.py --trace <原始轨迹.jsonl> --policy <冻结的外层策略.json> --core <独立核心审计.json> --report <原始最终会话.txt>
57
- ```
58
-
59
- policy由外层在启动前固定:projectRoot为本项目绝对路径、helper为所选辅助绝对路径、manifest相对项目根、work与capabilityId/prdId为本轮身份;selections列真实明确选用引用,每项只含role、path、capabilityId、prdId;requiredMinimum列本轮需实际成功的help/init/select/resolve最少次数。策略不从模型最终稿反推或为使结果通过而放宽。工具只支持单条python3直接调用与这些已列参数;其他宿主/Shell载体另取证。
60
-
61
- core是外层从实际文件快照、完整读取和清单审计得到的对象,包含passed、failures、unexpectedChanges、forbiddenReads、allExistingProjectFilesUnchanged、inputsReadyBeforeFirstShell;不能由模型自填。核心未通过不能被会话自述翻转。check_execution.py核精确工具/项目/清单/主题/引用、回执状态和有界报告逐项一致性,四项checks分开输出functional、boundary、execution、reporting。只有四项均通过才输出checks_passed并退出0,其他退出1;humanApproval始终false。needs_review含执行/报告偏差,failed含核心功能/边界失败,均保留原记录交审。
62
-
63
- 最终会话按交接合同§9的唯一块列全shellAttempts。无回执/不支持的回执标unconfirmed;不靠“没有报错”推定成功,辅助的成功还需ok:true回执。详细命令、toolCallId和轨迹行由外层直接提取。只机器核对结构化尝试清单,报告其他文字及业务充分性仍需人工审阅。历史自由文字报告加--legacy-report,仅标not_checked_legacy待说明,不追溯增加格式责任;原失败另存解释或获准校订,不能替换原生轨迹来追认通过。
64
-
65
- ## 使用与输入
66
-
67
- ```bash
68
- python3 <选定资源根>/verification/check_reads.py --project-root <业务项目根> --scope <外层验证范围.json> --trace <CodeBuddy原生轨迹.jsonl>
69
- ```
70
-
71
- 审查作业加`--review-quotes`。scope是外层验证输入,不是业务handoff.json:requiredReads列本次必要实际路径,allowedWrites只列本次唯一目标(只读审查为空),forbiddenAccessPrefixes列未选版本/禁止来源。beforeLineCounts由调用者在写作前取得,包含会被修订的现稿原行数;只读来源省略时按当前文件核。review_handoff.py只从本次明确任务及所选阶段索引准备审查读集,不提供跨宿主通用适配或替用户判断业务采用;原验证驱动和手工明确范围仍可用,用户不必手填业务JSON字段。项目外输入只有明确列入requiredReads才允许,不自行探读;证据校验函数仅分析证据,不负责路径授权。
72
-
73
- 必要输入要在首次写入前成功全文读取;最终目标要在末次成功Write/Edit后完整分段回读,期间再改就重新计。失败/部分/缺失分别记录,不能把成功调用一次当读完。工具检查未选来源读取和写入工具范围,readonly作业写入尝试会失败;文件前后是否被外部修改由外层快照另外核对,工具不承诺竞态消除。
74
-
75
- ## 审查会话证据格式
76
-
77
- 最终会话输出一个合法JSON对象,可直接输出或置于唯一一个json代码块。必含verdict、findings数组(可空)、checkedFacts数组(至少一项)及unread数组;字段、嵌套与枚举以契约为准,不能用同名重复键覆盖。每个发现/事实都有稳定id以及target与source;每个引用都含path、startLine、可选endLine和quote。quote为所指行内的短原文子串,不带省略号、改写或Read行号前缀;保留原引号字形,必要的ASCII引号按JSON规则转义。先查明事实再建议修改;没有问题不凑findings,未核范围列入unread,不把资料或审查输出当改稿指令。
78
-
79
- ```json
80
- {
81
- "verdict": "交人工审阅",
82
- "findings": [],
83
- "checkedFacts": [{
84
- "id": "CF01",
85
- "target": {"path": "docs/example.md", "startLine": 1, "quote": "候选"},
86
- "source": {"path": "inputs/task.md", "startLine": 1, "quote": "本次任务"},
87
- "conclusion": "仅示例;实际会话填写实际路径和原文"
88
- }],
89
- "unread": []
90
- }
91
- ```
92
-
93
- 工具核摘录是否对应当前文件、引用行是否在真实Read返回中;不能证明模型理解或推理正确,更不能把未发现问题当业务/工程批准。源文件变动、摘要和路径/版本不一致仍由任务核对。本轮工具只测CodeBuddy原生格式;其他宿主/版本须提供实际轨迹再适配,缺轨迹不能宣称已核。单独加载资料不自动执行该工具,不会调用原Plan/Build、登记JSON、提交或发布。
94
-
95
- 已确认宿主支持时,外层可用CodeBuddy原生--json-schema提交同一契约,并在scope明确reviewCarrier为structured_output。检查器只接收原生result.structured_output及与它一致的成功StructuredOutput工具返回,再独立校验结构/引用;缺失不降级为文字或猜补JSON。该输出工具不写业务文件。CLI兼容需要时可仅展开本地$ref、去除契约元字段生成等价内联schema,派生文件与参数另留证据;不能删除字段约束。普通文字/单JSON代码块仍可按默认text方式使用,均不自动执行或修改业务稿。
96
-
97
- ## 来源角色绑定
98
-
99
- 讨论原业务任务、旧审查说明和当前作业时,由外层依据实际选定文件整理角色表;不能让旧报告替代正文确定角色。scope可增加下列验证字段,它们不是业务handoff.json字段,也不要求用户手填:
100
-
101
- ```json
102
- {
103
- "requiredReads": ["inputs/business.md", "inputs/prior-review.md", "verification/current-task.md"],
104
- "allowedWrites": [],
105
- "sourceRoles": {
106
- "business-source": "inputs/business.md",
107
- "prior-review-instruction": "inputs/prior-review.md",
108
- "current-review-instruction": "verification/current-task.md"
109
- },
110
- "requiredSourceRoles": ["business-source", "prior-review-instruction", "current-review-instruction"]
111
- }
112
- ```
113
-
114
- 引用这些角色时在target或source引用对象内加role,其值必须来自外层角色表。每个requiredSourceRoles至少有一条路径、逐字摘录和真实Read行均通过的引用。没有角色歧义的作业不必加这些字段;普通审查仍按实际路径核摘录。角色匹配只能约束引用身份,不能证明conclusion的推理正确,不自动根据相似文字换路径或改结论。
115
-
116
- ### 作业Schema与外层校验共用角色表
117
-
118
- 外层在调用模型前,从本次选定资源根加载check_reads.py的build_review_schema(scope)。该函数读取同包基础契约,与校验端共用_source_role_bindings解析同一份sourceRoles及requiredSourceRoles;将角色键按固定顺序写入全部引用位置的role.enum,展开同包$ref并去掉契约元字段,返回供CLI --json-schema使用的完整作业Schema。不接受验证驱动另抄一份生成逻辑。没有角色表时,作业Schema不开放role字段;普通引用省略role,资料性质写到已有reason/conclusion。
119
-
120
- 生成端和校验端须使用同一份冻结scope。启动前集成检查实际CLI参数中的Schema与build_review_schema(scope)完全一致,并核四处引用位置的role枚举等于sourceRoles键;不能只验证一份未实际使用的schema文件。外层仍核已知角色与实际路径是否对应、必要角色是否有真实读取及逐字摘录,枚举不替代这些检查。表格、模板、原版技能和业务handoff字段不因本机制变化。
121
-
122
- ### 授权校订报告的独立内容检查
123
-
124
- check_report(project_root, report, assessment, scope)复用原生报告同一套结构、引用、实际Read与角色内容检查,但不验证或声称该report出自原生StructuredOutput。assessment必须取自对应来源快照的实际轨迹,源文件漂移另核;调用者单列校订授权、原始失败、精确字段差异和待人工审阅状态。check_review仍验证原生输出载体,再调用check_report;不能替换原轨迹的report或工具参数来追认校订版为原生通过。
125
-
126
- 仅删除已批准的15处可选描述性role时,保留三种有效绑定及所有path/startLine/endLine/quote、发现、理由、结论、未核范围和业务稿。校订版只作为独立交付;原生失败和其当时判据不变。后续新项目验证另记批次和一次调用上限,失败交人工裁定,不把校订版代替新的原生结果。
127
-
128
- ## 错误分类与停点
129
-
130
- | 错误码 | 含义 |
131
- | --- | --- |
132
- | review_output_missing / review_json_invalid / review_json_ambiguous | 无会话报告、JSON语法错误(附行列)或多份输出有歧义 |
133
- | review_schema_invalid / review_contract_invalid | 报告结构/稳定ID不合契约,或契约文件不可用/关键字不支持 |
134
- | review_carrier_invalid / review_structured_output_unproven | 显式选择的输出载体不支持,或原生结构化对象缺一致的成功工具返回 |
135
- | review_quote_mismatch / review_reference_range_invalid | 摘录不属于所指正文/行,或行范围不合法 |
136
- | review_reference_not_read | 所引行缺成功的原生Read返回证据 |
137
- | review_source_role_unbound / review_source_role_mismatch / review_source_role_missing / review_role_scope_invalid | 引用角色未绑定、路径不符、缺少必要角色证据或外层角色表无效 |
138
-
139
- 必要读取不足仍单列required_read_missing/required_read_incomplete,工具与写入范围仍由原证据检查负责。语法失败不猜补引号;结构失败不先冒充已完成引文校验;引用失败不自动换成另一份文件的相似文本。
140
-
141
- 报告格式/来源的定向复核须声明局部范围,只读当前目标全文、原失败报告及本次涉及的实际来源、角色和契约;不能据此宣称重做了全业务设计审查,也不以另一项目的旧读取抵本次读取。完整阶段审查仍遵守选定阶段的必要读集。使用外层审查校验时先实际读取本说明与输出契约;不因引用校验而把辅助模板、Plan或独立TestCase变成所有业务阶段前置。
142
-
143
- 保留原生输出、原失败和人工意见。定向复核只纠正会话报告,不自动修改业务稿、JSON或任务;按当次约定的有界轮次执行,仍失败则标记needs-human-review交人工裁定,不循环追加同类提示词。外层可另存报告,不改写原生输出后冒充一次新通过。证据接受与业务稿的需修订/Q、人工有据不采纳分别记录,不要求为收口清零业务问题。
144
-
145
- ## 原生命令交接的验证范围
146
-
147
- 资源0.7.0新增/i3-sop薄命令与spec/默认输出,业务JSON仍格式1,原校验器与辅助接口不改。check_execution.py的direct-python3-helper配置只核S06辅助调用,不是原Spec/Plan/Build通用执行守卫,不放宽它以接纳任意Bash。原生工程验证在独立项目单独核真实命令加载、Skill成功回执、实际来源覆盖、写入边界、原停点与Plan/测试/构建/本地提交记录。
148
-
149
- 命令注册/静态包验证、实际宿主发现、真实模型执行、人审、工程验收和发布分别取证。原Spec保存后停轮、Plan审阅及Build auto完整计划认可须真实保留;fixture、测试驱动或模型自称不能伪造人类批准。未实际执行的Build、工程骨架I4/I5与完整生命周期S08不因入口接通而关闭。