@haiyangbg/buildbeat 3.3.1 → 4.0.0

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 (100) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.en.md +20 -124
  3. package/README.md +20 -124
  4. package/SKILL.md +73 -84
  5. package/docs/CAPABILITY-MATRIX.md +30 -39
  6. package/docs/MIGRATION.md +93 -0
  7. package/docs/README.md +8 -37
  8. package/docs/RELEASING.md +4 -3
  9. package/docs/v2/RFC-0001-product-definition.md +3 -0
  10. package/docs/v2/RFC-0002-domain-model.md +3 -0
  11. package/docs/v2/RFC-0003-workflow-policy.md +3 -0
  12. package/docs/v2/SPEC-0001-events-v1.md +3 -0
  13. package/docs/v2/guide/00-how-to-talk.en.md +12 -58
  14. package/docs/v2/guide/00-how-to-talk.md +12 -58
  15. package/docs/v2/guide/01-quickstart.en.md +41 -112
  16. package/docs/v2/guide/01-quickstart.md +41 -112
  17. package/docs/v2/guide/02-workflow-guide.en.md +13 -123
  18. package/docs/v2/guide/02-workflow-guide.md +14 -122
  19. package/docs/v2/guide/03-policy-guide.en.md +6 -50
  20. package/docs/v2/guide/03-policy-guide.md +6 -50
  21. package/docs/v2/guide/04-adapter-guide.en.md +7 -60
  22. package/docs/v2/guide/04-adapter-guide.md +7 -60
  23. package/docs/v2/guide/05-worker-contract.en.md +6 -53
  24. package/docs/v2/guide/05-worker-contract.md +7 -52
  25. package/docs/v2/guide/06-evidence-guide.en.md +8 -44
  26. package/docs/v2/guide/06-evidence-guide.md +9 -43
  27. package/docs/v2/guide/07-approval-guide.en.md +8 -110
  28. package/docs/v2/guide/07-approval-guide.md +8 -121
  29. package/docs/v2/guide/09-security-boundaries.en.md +13 -37
  30. package/docs/v2/guide/09-security-boundaries.md +13 -37
  31. package/docs/v2/guide/10-recovery.en.md +12 -96
  32. package/docs/v2/guide/10-recovery.md +12 -96
  33. package/docs/v2/guide/11-session-handoff.en.md +7 -78
  34. package/docs/v2/guide/11-session-handoff.md +7 -78
  35. package/docs/v2/guide/12-without-runtime.en.md +46 -0
  36. package/docs/v2/guide/12-without-runtime.md +46 -0
  37. package/docs/v2/guide/README.en.md +16 -36
  38. package/docs/v2/guide/README.md +16 -36
  39. package/example/AGENTS.md +15 -69
  40. package/example/README.md +8 -23
  41. package/example/delivery/envelope/prompts/builder.md +3 -1
  42. package/example/delivery/envelope/prompts/fixer.md +3 -1
  43. package/example/delivery/envelope/prompts/reviewer.md +3 -1
  44. package/example//346/214/207/346/214/245/345/217/260.md +8 -40
  45. package/package.json +2 -1
  46. package/src/v2/adapters/notification-payload.js +40 -0
  47. package/src/v2/adapters/shell.js +3 -1
  48. package/src/v2/cli/run-config-check.js +129 -45
  49. package/src/v2/cli/run.js +821 -252
  50. package/src/v2/engine/reducer.js +2 -0
  51. package/src/v2/engine/risk-preset.js +14 -47
  52. package/src/v2/engine/workflow.js +177 -185
  53. package/src/v2/evidence/collector.js +12 -1
  54. package/src/v2/evidence/image.js +346 -0
  55. package/src/v2/policy/policy.js +219 -286
  56. package/src/v2/presets/software-delivery.yaml +1 -16
  57. package/src/v2/runtime/cache.js +22 -1
  58. package/src/v2/runtime/decisions.js +185 -31
  59. package/src/v2/runtime/findings.js +4 -1
  60. package/src/v2/runtime/notify.js +42 -36
  61. package/src/v2/runtime/orchestrator.js +811 -297
  62. package/src/v2/runtime/overview.js +163 -34
  63. package/src/v2/runtime/release.js +233 -0
  64. package/src/v2/runtime/run-record.js +1 -0
  65. package/src/v2/workspace/workspace-manager.js +8 -0
  66. package/templates/v2/AGENTS.md +15 -69
  67. package/templates/v2/BUILDBEAT.md +2 -13
  68. package/templates/v2/envelope/prompts/builder.md +3 -1
  69. package/templates/v2/envelope/prompts/fixer.md +3 -1
  70. package/templates/v2/envelope/prompts/reviewer.md +3 -1
  71. package/templates/v2/run-config.example.yaml +7 -5
  72. package/templates/v2/work.example.md +16 -0
  73. package/templates/v2//346/214/207/346/214/245/345/217/260.md +8 -40
  74. package/docs/v2/skill/01-principles.md +0 -26
  75. package/docs/v2/skill/02-project-layout.md +0 -31
  76. package/docs/v2/skill/03-collaboration-rules.md +0 -45
  77. package/docs/v2/skill/04-rhythm-and-rituals.md +0 -104
  78. package/docs/v2/skill/05-red-lines.md +0 -13
  79. package/docs/v2/skill/06-bootstrap-and-takeover.md +0 -84
  80. package/docs/v2/skill/07-templates-and-lessons.md +0 -24
  81. package/example/.buildbeat/observe.yaml +0 -31
  82. package/src/v2/observe/observe-config.js +0 -194
  83. package/src/v2/observe/observe-reducer.js +0 -117
  84. package/src/v2/observe/observe.js +0 -427
  85. package/src/v2/presets/observe.yaml +0 -45
  86. package/src/v2/presets/policies/ui-render-gate.yaml +0 -13
  87. package/src/v2/presets/release-readback.yaml +0 -36
  88. package/src/v2/presets/risk/controlled.yaml +0 -39
  89. package/src/v2/presets/risk/fast.yaml +0 -19
  90. package/src/v2/presets/risk/release.yaml +0 -21
  91. package/src/v2/presets/risk/standard.yaml +0 -28
  92. package/templates/ARCHITECTURE.md +0 -50
  93. package/templates/contracts/PROTOCOL.md +0 -28
  94. package/templates/pm/adr/ADR-0000-template.md +0 -25
  95. package/templates/pm/adr/README.md +0 -15
  96. package/templates/pm/decisions.md +0 -11
  97. package/templates/standards/CODE.md +0 -23
  98. package/templates/standards/DESIGN.md +0 -36
  99. package/templates/standards/REVIEW.md +0 -20
  100. package/templates/standards/STACK.md +0 -31
@@ -1,427 +0,0 @@
1
- // observe v0 runner per docs/v2/RFC-0003-workflow-policy.md §8 (frozen
2
- // semantics): providers produce contract-shaped Evidence (unreachable data is
3
- // unverified, never silence); bands route log → readonly diagnose → intent
4
- // DRAFT enqueue (never auto-executed); dismiss feeds back into drafting.
5
- // Intent drafts and triage verdicts live in the Git plane so deleting
6
- // .buildbeat/runtime/ loses no decision (invariant 23). One invocation runs
7
- // one cycle; periodic operation is host-level (cron/CI) in v0.
8
-
9
- import { createHash } from "node:crypto";
10
- import {
11
- existsSync,
12
- mkdirSync,
13
- readFileSync,
14
- readdirSync,
15
- writeFileSync,
16
- } from "node:fs";
17
- import { join, resolve } from "node:path";
18
-
19
- import { createShellAdapter } from "../adapters/shell.js";
20
- import { INTENT_DRAFT_STATES, TRIAGE_ACTIONS } from "../domain/model.js";
21
- import { collectCommandEvidence } from "../evidence/collector.js";
22
- import { EventLedger } from "../storage/event-ledger.js";
23
- import { loadObserveConfig, severityRank } from "./observe-config.js";
24
- import { OBSERVE_REDUCER } from "./observe-reducer.js";
25
- import { toRepoRef } from "../runtime/repo-ref.js";
26
-
27
- const OBSERVE_IDS = { run: "OBSERVE", work: "OBSERVE" };
28
- const KERNEL_ACTOR = { kind: "kernel", id: "observe" };
29
- const HEADER_PREFIX = "<!-- buildbeat-intent ";
30
- const HEADER_SUFFIX = " -->";
31
-
32
- export class ObserveError extends Error {
33
- constructor(message) {
34
- super(message);
35
- this.name = "ObserveError";
36
- }
37
- }
38
-
39
- function observeLedgerPath(repoRoot) {
40
- return join(repoRoot, ".buildbeat", "runtime", "observe", "events.jsonl");
41
- }
42
-
43
- export function openObserveLedger(repoRoot) {
44
- return EventLedger.open(observeLedgerPath(repoRoot), OBSERVE_REDUCER);
45
- }
46
-
47
- function intentsDir(repoRoot) {
48
- return join(repoRoot, "delivery", "observe", "intents");
49
- }
50
-
51
- function parseIntentHeader(filePath, text) {
52
- const firstLine = text.split("\n", 1)[0] ?? "";
53
- if (!firstLine.startsWith(HEADER_PREFIX) || !firstLine.endsWith(HEADER_SUFFIX)) {
54
- throw new ObserveError(
55
- `intent draft ${filePath} has no buildbeat-intent header line; fix or remove the file`,
56
- );
57
- }
58
- const body = firstLine.slice(HEADER_PREFIX.length, -HEADER_SUFFIX.length);
59
- let header;
60
- try {
61
- header = JSON.parse(body);
62
- } catch {
63
- throw new ObserveError(`intent draft ${filePath} header is not valid JSON`);
64
- }
65
- if (!INTENT_DRAFT_STATES.includes(header.status)) {
66
- throw new ObserveError(
67
- `intent draft ${filePath} has unknown status: ${header.status}`,
68
- );
69
- }
70
- return header;
71
- }
72
-
73
- export function readIntentDrafts(repoRoot) {
74
- const dir = intentsDir(repoRoot);
75
- if (!existsSync(dir)) {
76
- return [];
77
- }
78
- const drafts = [];
79
- for (const name of readdirSync(dir).sort()) {
80
- if (!name.endsWith(".md")) {
81
- continue;
82
- }
83
- const filePath = join(dir, name);
84
- const text = readFileSync(filePath, "utf8");
85
- drafts.push({ file: name, path: filePath, header: parseIntentHeader(filePath, text), text });
86
- }
87
- return drafts;
88
- }
89
-
90
- function renderHeader(header) {
91
- return `${HEADER_PREFIX}${JSON.stringify(header)}${HEADER_SUFFIX}`;
92
- }
93
-
94
- function writeDraft(filePath, header, body) {
95
- writeFileSync(filePath, `${renderHeader(header)}\n${body}`, "utf8");
96
- }
97
-
98
- function rewriteHeader(draft, header, appendLine) {
99
- const lines = draft.text.split("\n");
100
- lines[0] = renderHeader(header);
101
- let text = lines.join("\n");
102
- if (appendLine) {
103
- text = `${text.replace(/\n+$/, "\n")}${appendLine}\n`;
104
- }
105
- writeFileSync(draft.path, text, "utf8");
106
- }
107
-
108
- function fingerprintFor(provider, status) {
109
- const key = [provider.id, provider.evidence.kind, provider.evidence.subject, status].join("|");
110
- return createHash("sha256").update(key, "utf8").digest("hex").slice(0, 16);
111
- }
112
-
113
- function severityFor(provider, status) {
114
- if (status === "passed") {
115
- return null;
116
- }
117
- return status === "failed" ? provider.severity.failed : provider.severity.unverified;
118
- }
119
-
120
- function execCommand({ name, command, args, timeoutMs, repoRoot, input }) {
121
- const adapter = createShellAdapter({ name, command, args, timeoutMs });
122
- return adapter.execute({
123
- step: name,
124
- worker: name,
125
- workspacePath: repoRoot,
126
- input,
127
- timeoutMs,
128
- });
129
- }
130
-
131
- function recordEvidence({ ledger, config, cycle, provider, execResult, kind, step, grade = "L2" }) {
132
- const record = collectCommandEvidence({
133
- runtimeDir: join(config.repoRoot, ".buildbeat", "runtime", "observe"),
134
- runId: `cycle-${String(cycle).padStart(4, "0")}`,
135
- step,
136
- attempt: 1,
137
- execResult,
138
- subject: provider.evidence.subject,
139
- kind,
140
- grade,
141
- });
142
- ledger.append({
143
- type: "EVIDENCE_RECORDED",
144
- actor: { kind: "provider", id: provider.id },
145
- data: {
146
- evidenceRef: toRepoRef(config.repoRoot, record.location),
147
- kind: record.kind,
148
- subject: record.subject,
149
- digest: record.digest,
150
- status: record.status,
151
- grade: record.grade,
152
- provider: provider.id,
153
- cycle,
154
- },
155
- ...OBSERVE_IDS,
156
- });
157
- return record;
158
- }
159
-
160
- function draftBody({ provider, severity, record, fingerprint }) {
161
- return `# Intent 草稿:${provider.id} 在 ${provider.evidence.subject} 上发现 ${provider.evidence.kind} 异常
162
-
163
- - 来源 provider:\`${provider.id}\`(severity ${severity})
164
- - 主题:\`${provider.evidence.subject}\`
165
- - 证据:\`${record.digest}\`(${record.status};日志见 runtime observe 面,runtime 可删,digest 永续)
166
- - 指纹:\`${fingerprint}\`
167
-
168
- ## 下一步(人分诊后才会发生任何执行)
169
-
170
- - \`fix_now\`:接受后由人工带入 software-delivery Run(本草稿绝不自动执行);
171
- - \`schedule\`:排期处理;
172
- - \`dismiss\`:驳回——同指纹在严重度升级前不再重复入队。
173
-
174
- 分诊命令:
175
-
176
- \`\`\`
177
- node src/v2/cli/run.js observe triage --repo <repo> --intent <本文件相对路径> --action fix_now|schedule|dismiss --by <name>
178
- \`\`\`
179
- `;
180
- }
181
-
182
- function handleIntentBand({ ledger, config, cycle, provider, severity, record, fingerprint, now }) {
183
- const dir = intentsDir(config.repoRoot);
184
- const drafts = readIntentDrafts(config.repoRoot);
185
- const existing = drafts.find((draft) => draft.header.fingerprint === fingerprint);
186
- if (existing) {
187
- const header = existing.header;
188
- if (header.status === "dismissed") {
189
- if (severityRank(severity) <= severityRank(header.dismissedSeverity ?? header.severity)) {
190
- return { outcome: "suppressed", intentRef: relativeIntentRef(config.repoRoot, existing.path) };
191
- }
192
- const reopened = {
193
- ...header,
194
- status: "draft",
195
- severity,
196
- evidenceDigest: record.digest,
197
- updated: now,
198
- };
199
- delete reopened.dismissedSeverity;
200
- delete reopened.triage;
201
- rewriteHeader(
202
- existing,
203
- reopened,
204
- `> ${now}:严重度升级为 ${severity}(此前以 ${header.dismissedSeverity ?? header.severity} 被 dismiss),草稿重新开启。`,
205
- );
206
- const intentRef = relativeIntentRef(config.repoRoot, existing.path);
207
- ledger.append({
208
- type: "INTENT_DRAFTED",
209
- actor: KERNEL_ACTOR,
210
- data: { intentRef, provider: provider.id, severity, fingerprint },
211
- ...OBSERVE_IDS,
212
- });
213
- return { outcome: "reopened", intentRef };
214
- }
215
- if (severityRank(severity) > severityRank(header.severity)) {
216
- rewriteHeader(existing, { ...header, severity, evidenceDigest: record.digest, updated: now });
217
- }
218
- return { outcome: "already-queued", intentRef: relativeIntentRef(config.repoRoot, existing.path) };
219
- }
220
- mkdirSync(dir, { recursive: true });
221
- const fileName = `INTENT-${fingerprint}.md`;
222
- const filePath = join(dir, fileName);
223
- const header = {
224
- id: fingerprint,
225
- fingerprint,
226
- provider: provider.id,
227
- kind: provider.evidence.kind,
228
- subject: provider.evidence.subject,
229
- severity,
230
- status: "draft",
231
- evidenceDigest: record.digest,
232
- created: now,
233
- updated: now,
234
- };
235
- writeDraft(filePath, header, draftBody({ provider, severity, record, fingerprint }));
236
- const intentRef = relativeIntentRef(config.repoRoot, filePath);
237
- ledger.append({
238
- type: "INTENT_DRAFTED",
239
- actor: KERNEL_ACTOR,
240
- data: { intentRef, provider: provider.id, severity, fingerprint },
241
- ...OBSERVE_IDS,
242
- });
243
- return { outcome: "drafted", intentRef };
244
- }
245
-
246
- function relativeIntentRef(repoRoot, filePath) {
247
- return filePath.startsWith(repoRoot) ? filePath.slice(repoRoot.length + 1) : filePath;
248
- }
249
-
250
- export function runObserveCycle({ configPath, now = new Date().toISOString() }) {
251
- const config = loadObserveConfig(configPath);
252
- const ledger = openObserveLedger(config.repoRoot);
253
- const cycle = ledger.state.cycles + 1;
254
- ledger.append({
255
- type: "OBSERVE_CYCLE_STARTED",
256
- actor: KERNEL_ACTOR,
257
- data: { cycle, configDigest: config.configDigest },
258
- ...OBSERVE_IDS,
259
- });
260
- const results = [];
261
- for (const provider of config.providers) {
262
- const execResult = execCommand({
263
- name: `provider:${provider.id}`,
264
- command: provider.command,
265
- args: provider.args,
266
- timeoutMs: provider.timeoutMs ?? 600000,
267
- repoRoot: config.repoRoot,
268
- input: { provider: provider.id, cycle },
269
- });
270
- const record = recordEvidence({
271
- ledger,
272
- config,
273
- cycle,
274
- provider,
275
- execResult,
276
- kind: provider.evidence.kind,
277
- step: provider.id,
278
- });
279
- const severity = severityFor(provider, record.status);
280
- const result = {
281
- provider: provider.id,
282
- status: record.status,
283
- severity,
284
- bands: [],
285
- intent: null,
286
- };
287
- if (severity !== null) {
288
- const fingerprint = fingerprintFor(provider, record.status);
289
- for (const band of config.bands) {
290
- if (severityRank(severity) < severityRank(band.minSeverity)) {
291
- continue;
292
- }
293
- ledger.append({
294
- type: "BAND_TRIGGERED",
295
- actor: KERNEL_ACTOR,
296
- data: { provider: provider.id, band: band.level, severity, fingerprint },
297
- ...OBSERVE_IDS,
298
- });
299
- result.bands.push(band.level);
300
- if (band.level === "diagnose") {
301
- const diagnoseResult = provider.diagnose
302
- ? execCommand({
303
- name: `diagnose:${provider.id}`,
304
- command: provider.diagnose.command,
305
- args: provider.diagnose.args,
306
- timeoutMs: provider.diagnose.timeoutMs ?? 600000,
307
- repoRoot: config.repoRoot,
308
- input: { provider: provider.id, cycle, band: "diagnose" },
309
- })
310
- : {
311
- adapter: "observe",
312
- command: "(no diagnose command configured)",
313
- exitCode: null,
314
- signal: null,
315
- stdout: "",
316
- stderr: "diagnose band triggered but the provider configures no diagnose command",
317
- timedOut: false,
318
- spawnError: "no diagnose command configured",
319
- startedAt: now,
320
- finishedAt: now,
321
- };
322
- recordEvidence({
323
- ledger,
324
- config,
325
- cycle,
326
- provider,
327
- execResult: diagnoseResult,
328
- kind: "diagnosis",
329
- step: `${provider.id}-diagnose`,
330
- });
331
- }
332
- if (band.level === "intent") {
333
- result.intent = handleIntentBand({
334
- ledger,
335
- config,
336
- cycle,
337
- provider,
338
- severity,
339
- record,
340
- fingerprint,
341
- now,
342
- });
343
- }
344
- }
345
- }
346
- results.push(result);
347
- }
348
- ledger.append({
349
- type: "OBSERVE_CYCLE_FINISHED",
350
- actor: KERNEL_ACTOR,
351
- data: { cycle, providersRun: config.providers.length },
352
- ...OBSERVE_IDS,
353
- });
354
- return {
355
- cycle,
356
- ledgerPath: ledger.path,
357
- ledgerRef: toRepoRef(config.repoRoot, ledger.path),
358
- results,
359
- state: ledger.state,
360
- };
361
- }
362
-
363
- export function triageIntent({ repoRoot, intentRef, action, by = "unknown", note, now = new Date().toISOString() }) {
364
- if (!TRIAGE_ACTIONS.includes(action)) {
365
- throw new ObserveError(`action must be one of ${TRIAGE_ACTIONS.join("|")}, got: ${action}`);
366
- }
367
- const root = resolve(repoRoot);
368
- const candidatePaths = [
369
- resolve(root, intentRef),
370
- join(intentsDir(root), intentRef),
371
- join(intentsDir(root), `${intentRef}.md`),
372
- ];
373
- const filePath = candidatePaths.find((path) => existsSync(path));
374
- if (!filePath) {
375
- throw new ObserveError(`intent draft not found: ${intentRef}`);
376
- }
377
- const text = readFileSync(filePath, "utf8");
378
- const header = parseIntentHeader(filePath, text);
379
- const nextStatus = action === "fix_now" ? "accepted" : action === "schedule" ? "scheduled" : "dismissed";
380
- const nextHeader = {
381
- ...header,
382
- status: nextStatus,
383
- updated: now,
384
- triage: { action, by, at: now, ...(note ? { note } : {}) },
385
- ...(action === "dismiss" ? { dismissedSeverity: header.severity } : {}),
386
- };
387
- if (action !== "dismiss") {
388
- delete nextHeader.dismissedSeverity;
389
- }
390
- rewriteHeader({ path: filePath, text }, nextHeader);
391
- const ledger = openObserveLedger(root);
392
- ledger.append({
393
- type: "TRIAGE_RECORDED",
394
- actor: { kind: "human", id: by },
395
- data: {
396
- intentRef: relativeIntentRef(root, filePath),
397
- action,
398
- fingerprint: header.fingerprint,
399
- },
400
- ...OBSERVE_IDS,
401
- });
402
- return {
403
- intentRef: relativeIntentRef(root, filePath),
404
- status: nextStatus,
405
- fingerprint: header.fingerprint,
406
- suggestion:
407
- action === "fix_now"
408
- ? "accepted: start a software-delivery run for this intent (run.js start --config <run-config.yaml>) — the draft never executes itself"
409
- : null,
410
- };
411
- }
412
-
413
- export function observeStatus({ repoRoot }) {
414
- const root = resolve(repoRoot);
415
- const ledger = openObserveLedger(root);
416
- const drafts = readIntentDrafts(root);
417
- return {
418
- ledgerPath: ledger.path,
419
- corruption: ledger.corruption,
420
- cycles: ledger.state.cycles,
421
- providers: ledger.state.providers,
422
- intents: drafts.map((draft) => ({
423
- file: relativeIntentRef(root, draft.path),
424
- ...draft.header,
425
- })),
426
- };
427
- }
@@ -1,45 +0,0 @@
1
- # observe workflow preset per docs/v2/RFC-0003-workflow-policy.md §8 (frozen
2
- # shape; block form — the strict YAML subset has no inline {}/[]).
3
- # One `observe run` invocation = one cycle; scheduling is host-level in v0
4
- # (cron/CI invoking the CLI). Copy to <repo>/.buildbeat/observe.yaml and
5
- # point commands at the project's real probes.
6
- kind: workflow
7
- version: 1
8
- name: observe
9
- providers:
10
- - id: drift-check
11
- command: bash
12
- args:
13
- - scripts/drift-check.sh
14
- schedule: manual
15
- evidence:
16
- kind: drift
17
- subject: deploy-unit
18
- severity:
19
- failed: error
20
- unverified: warn
21
- diagnose:
22
- command: bash
23
- args:
24
- - scripts/live-status.sh
25
- - id: live-status
26
- command: bash
27
- args:
28
- - scripts/live-status.sh
29
- schedule: manual
30
- evidence:
31
- kind: runtime-health
32
- subject: deploy-unit
33
- bands:
34
- - level: log
35
- when: "severity >= info"
36
- - level: diagnose
37
- when: "severity >= error"
38
- - level: intent
39
- when: "severity >= error"
40
- triage:
41
- actions:
42
- - fix_now
43
- - schedule
44
- - dismiss
45
- dismissFeedback: bands
@@ -1,13 +0,0 @@
1
- # Invariant 22 (lessons.md「静态稿拍板 → 返工螺旋」, machine form): a UI delivery's spec approval
2
- # subject must include renderable proof — a screenshot with a digest —
3
- # before a human may stamp it. Static prose is not an approvable design.
4
- kind: policy
5
- version: 1
6
- name: ui-render-gate
7
- type: transition
8
- appliesTo: enter-spec
9
- enforcement: LOCAL_ENFORCED
10
- rule:
11
- evidence.exists:
12
- kind: screenshot
13
- minGrade: L2
@@ -1,36 +0,0 @@
1
- # Release readback lane (iteration 08, C8). The kernel still has no deploy
2
- # capability (invariant 20): every production action stays a human's. What
3
- # this lane fixes is the bookkeeping around it — a pilot go-live was
4
- # ledgered by hand as forty "step N readback" commits. Here each step is a
5
- # read-only command that proves the world is in the expected state; every
6
- # failure stops for a human (maxAttempts 1, no fix edge), and evidence is
7
- # graded L4 because it was read from the real environment.
8
- #
9
- # Shape: preflight (before the action) → [human performs the action; the
10
- # risk preset "release" stops here] → apply-readback (proves it happened) →
11
- # observe (proves it is healthy) → wait-close (human closes the window).
12
- kind: workflow
13
- version: 1
14
- name: release-readback
15
- entry: preflight
16
- steps:
17
- - id: preflight
18
- worker: readback
19
- readonly: true
20
- grade: L4
21
- - id: apply-readback
22
- worker: readback
23
- readonly: true
24
- grade: L4
25
- - id: observe
26
- worker: observe
27
- readonly: true
28
- grade: L4
29
- - id: wait-close
30
- terminal:
31
- - wait-close
32
- budgets:
33
- maxAttempts:
34
- preflight: 1
35
- apply-readback: 1
36
- observe: 1
@@ -1,39 +0,0 @@
1
- # Controlled lane: intent AND plan must be accepted artifacts before build;
2
- # merge stays human behind a stricter evidence floor. The release gate from
3
- # the controlled track is out of MVP scope (no deploy capability exists) and
4
- # returns with observe/release work in M5.
5
- kind: risk-preset
6
- version: 1
7
- name: controlled
8
- policies:
9
- - kind: policy
10
- version: 1
11
- name: intent-accepted
12
- type: pre
13
- appliesTo: build
14
- enforcement: LOCAL_ENFORCED
15
- rule:
16
- artifact.accepted:
17
- artifact: intent
18
- - kind: policy
19
- version: 1
20
- name: plan-accepted
21
- type: pre
22
- appliesTo: build
23
- enforcement: LOCAL_ENFORCED
24
- rule:
25
- artifact.accepted:
26
- artifact: plan
27
- - kind: policy
28
- version: 1
29
- name: merge-evidence-floor
30
- type: transition
31
- appliesTo: enter-wait-merge
32
- enforcement: LOCAL_ENFORCED
33
- rule:
34
- all:
35
- - evidence.exists:
36
- kind: command
37
- minGrade: L2
38
- - finding.maxSeverity:
39
- atMost: P3
@@ -1,19 +0,0 @@
1
- # Fast lane: automatic plan/build/verify/review, human merge decision only.
2
- # The merge gate still demands read-back evidence and a clean review.
3
- kind: risk-preset
4
- version: 1
5
- name: fast
6
- policies:
7
- - kind: policy
8
- version: 1
9
- name: merge-evidence-floor
10
- type: transition
11
- appliesTo: enter-wait-merge
12
- enforcement: LOCAL_ENFORCED
13
- rule:
14
- all:
15
- - evidence.exists:
16
- kind: command
17
- minGrade: L2
18
- - finding.maxSeverity:
19
- atMost: P2
@@ -1,21 +0,0 @@
1
- # Release lane (iteration 08, C8): pairs with the release-readback workflow.
2
- # Automation stops before apply-readback — that pause is where the human
3
- # performs the production action — and the window may only close behind L4
4
- # evidence read from the real environment. (No finding floor: the lane has
5
- # no reviewer step, and a rule that cannot be satisfied is not a gate.)
6
- kind: risk-preset
7
- version: 1
8
- name: release
9
- stopAt:
10
- - apply-readback
11
- policies:
12
- - kind: policy
13
- version: 1
14
- name: close-evidence-floor
15
- type: transition
16
- appliesTo: enter-wait-close
17
- enforcement: LOCAL_ENFORCED
18
- rule:
19
- evidence.exists:
20
- kind: command
21
- minGrade: L4
@@ -1,28 +0,0 @@
1
- # Standard lane: the plan must be an accepted artifact (digest-bound) before
2
- # build starts; merge stays a human decision behind the evidence floor.
3
- kind: risk-preset
4
- version: 1
5
- name: standard
6
- policies:
7
- - kind: policy
8
- version: 1
9
- name: plan-accepted
10
- type: pre
11
- appliesTo: build
12
- enforcement: LOCAL_ENFORCED
13
- rule:
14
- artifact.accepted:
15
- artifact: plan
16
- - kind: policy
17
- version: 1
18
- name: merge-evidence-floor
19
- type: transition
20
- appliesTo: enter-wait-merge
21
- enforcement: LOCAL_ENFORCED
22
- rule:
23
- all:
24
- - evidence.exists:
25
- kind: command
26
- minGrade: L2
27
- - finding.maxSeverity:
28
- atMost: P2
@@ -1,50 +0,0 @@
1
- # ARCHITECTURE.md — <项目名> 全栈总图(AI 会话接手按需读这份)
2
-
3
- > 一句话:<项目是什么、给谁用、核心取舍>。
4
- > 本文是**顶层总图**:架构链路 + 文件夹 + 基础设施标识。**按需读,不自动装载**(自动装载的是根 `AGENTS.md`)。各子系统细节在各代码子仓自己的 `AGENTS.md`/`README.md`,文末有索引。
5
- > 🔴 凭据(key / DB 密码 / token)一律**只标位置、不写值**。任何会话不得把凭据写进任何文档或代码。
6
-
7
- ## 0. 架构链路
8
-
9
- ```
10
- <用户入口>
11
- ▼
12
- <前端/客户端>(<仓名>,<部署单元>)
13
- ▼
14
- <网关/服务A> ──▶ <审计/可观测>
15
- ├──▶ <服务B>
16
- └──▶ <服务C>
17
- ```
18
-
19
- ## 1. 文件夹
20
-
21
- ```
22
- <项目根>/
23
- ├── AGENTS.md / ARCHITECTURE.md / 指挥台.md / BUILDBEAT.md / contracts/ / delivery/ / pm/decisions.md
24
- ├── <代码仓1>/ # ★ <说明>;详见其 AGENTS.md
25
- └── <代码仓2>/ # ★ <说明>
26
- ```
27
-
28
- ## 2. 基础设施标识(资源变动时更新本节;细节别往这塞)
29
-
30
- | 项 | 值 | 说明 |
31
- |---|---|---|
32
- | <部署平台/区域> | | |
33
- | <应用/服务清单:名称+ID+实例> | | |
34
- | <入口/域名/LB> | | |
35
- | <数据库/缓存> | | |
36
-
37
- ### 凭据位置(🔴 只读取,不外泄、不写值)
38
- - <服务A 凭据> → `<路径/获取命令>`
39
- - <运行时 env 实查> → `<平台 CLI 命令>`
40
-
41
- ## 3. 红线
42
- 见根 `AGENTS.md §3`(凭据/不 add -A/不未授权部署/CHANGELOG/写者≠审者)。
43
-
44
- ## 4. 子项目文档索引(改哪块先读哪份)
45
-
46
- | 改动范围 | 先读 |
47
- |---|---|
48
- | <代码仓1> | `<代码仓1>/AGENTS.md` + `CHANGELOG.md` |
49
- | <代码仓2> | `<代码仓2>/README.md` |
50
- | 跨边界契约 | `contracts/PROTOCOL.md` |