iterate-plugin 3.9.2 → 3.11.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.
- package/README.md +8 -7
- package/README.zh-CN.md +4 -4
- package/dist/report-render.js +33 -2
- package/dist/tools/decision-log.js +628 -144
- package/dist/tools/history.js +4 -2
- package/dist/tools/prune.js +85 -97
- package/dist/tools/report.js +20 -8
- package/dist/types.js +24 -1
- package/lib/client.js +39 -5
- package/package.json +24 -12
- package/src/client/i18n.ts +3 -0
- package/src/client/index.ts +15 -4
- package/src/report-render.ts +69 -3
- package/src/tools/decision-log.ts +736 -148
- package/src/tools/history.ts +4 -2
- package/src/tools/prune.ts +104 -95
- package/src/tools/report.ts +19 -8
- package/src/types.ts +41 -12
package/src/client/index.ts
CHANGED
|
@@ -113,6 +113,7 @@ import {
|
|
|
113
113
|
import {
|
|
114
114
|
renderRunReportMarkdown, // Markdown run report (PR-pasteable)
|
|
115
115
|
renderRunReportSarif, // SARIF 2.1.0 log (CI / Code Scanning)
|
|
116
|
+
renderRunReportJson, // full normalized run model (CI dashboards / PR bots)
|
|
116
117
|
runReportFilename, // deterministic download filename
|
|
117
118
|
} from '../report-render.ts'
|
|
118
119
|
import {
|
|
@@ -2419,12 +2420,14 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
2419
2420
|
* SARIF 2.1.0 (CI / GitHub Code Scanning). Rendered from the SAME shared
|
|
2420
2421
|
* data model as the `iterate_report` tool, so the button and the tool can
|
|
2421
2422
|
* never disagree. Download-first with a copy fallback, like `exportObservatory`. */
|
|
2422
|
-
const exportReport = (format: 'markdown' | 'sarif') => {
|
|
2423
|
+
const exportReport = (format: 'markdown' | 'sarif' | 'json') => {
|
|
2423
2424
|
if (!manifest) return
|
|
2424
2425
|
const extra = { qualityGate, experienceBank, defenseEvents }
|
|
2425
2426
|
const content = format === 'sarif'
|
|
2426
2427
|
? JSON.stringify(renderRunReportSarif(manifest, extra), null, 2)
|
|
2427
|
-
:
|
|
2428
|
+
: format === 'json'
|
|
2429
|
+
? JSON.stringify(renderRunReportJson(manifest, extra), null, 2)
|
|
2430
|
+
: renderRunReportMarkdown(manifest, extra, { lang: getLang() })
|
|
2428
2431
|
const filename = runReportFilename(format)
|
|
2429
2432
|
const key = `report-${format}`
|
|
2430
2433
|
const flash = () => {
|
|
@@ -3481,8 +3484,8 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
3481
3484
|
title: t('将观测台全部数据导出为 JSON(优先下载,失败则复制)'),
|
|
3482
3485
|
}, copiedKey === 'export' ? t('已导出') : t('导出 JSON')),
|
|
3483
3486
|
// v3.9: portable report export — Markdown for a PR body, SARIF for
|
|
3484
|
-
// CI / GitHub Code Scanning
|
|
3485
|
-
// the `iterate_report` tool.
|
|
3487
|
+
// CI / GitHub Code Scanning, JSON for CI dashboards / PR bots.
|
|
3488
|
+
// Rendered from the same data model as the `iterate_report` tool.
|
|
3486
3489
|
React.createElement('button', {
|
|
3487
3490
|
className: 'iterate-btn', 'data-copied': copiedKey === 'report-markdown' ? '' : undefined,
|
|
3488
3491
|
onClick: () => exportReport('markdown'),
|
|
@@ -3493,6 +3496,14 @@ function ObservatoryPanel(props: SlotProps) {
|
|
|
3493
3496
|
onClick: () => exportReport('sarif'),
|
|
3494
3497
|
title: t('导出 SARIF 2.1.0(供 CI / GitHub Code Scanning 消费)'),
|
|
3495
3498
|
}, copiedKey === 'report-sarif' ? t('已导出') : t('导出 SARIF')),
|
|
3499
|
+
// v3.11: lossless JSON export — the complete normalized run model
|
|
3500
|
+
// (findings + fixes + validations + quality gate + experience bank
|
|
3501
|
+
// + defense events) for CI dashboards and PR bots.
|
|
3502
|
+
React.createElement('button', {
|
|
3503
|
+
className: 'iterate-btn', 'data-copied': copiedKey === 'report-json' ? '' : undefined,
|
|
3504
|
+
onClick: () => exportReport('json'),
|
|
3505
|
+
title: t('导出完整运行模型的 JSON(供 CI 仪表盘 / PR 机器人消费,无损格式)'),
|
|
3506
|
+
}, copiedKey === 'report-json' ? t('已导出') : t('导出 JSON 报告')),
|
|
3496
3507
|
// Gap #4: `.iterate/` artifacts are unreachable from the browser;
|
|
3497
3508
|
// this copies the one instruction that makes the model echo them
|
|
3498
3509
|
// into the session stream (which is what every panel scans).
|
package/src/report-render.ts
CHANGED
|
@@ -20,10 +20,15 @@
|
|
|
20
20
|
* - **sarif** — SARIF 2.1.0 for CI / GitHub Code Scanning (ruleId =
|
|
21
21
|
* review dimension, severity → error | warning | note, file+line
|
|
22
22
|
* locations, suggested fix attached).
|
|
23
|
+
* - **json** — the complete normalized run model (see {@link RunReportData})
|
|
24
|
+
* as a machine-readable JSON document. This is the ONLY format that carries
|
|
25
|
+
* the full model (findings + fixes + validations + quality gate + experience
|
|
26
|
+
* bank + defense events) without lossy projection, so CI dashboards and PR
|
|
27
|
+
* bots can consume the exact same facts the human report shows.
|
|
23
28
|
*/
|
|
24
29
|
|
|
25
30
|
/** Report formats accepted by the tool and the export buttons. */
|
|
26
|
-
export const REPORT_FORMATS = ['markdown', 'sarif'] as const
|
|
31
|
+
export const REPORT_FORMATS = ['markdown', 'sarif', 'json'] as const
|
|
27
32
|
|
|
28
33
|
/** Severity ordering (lowest index = most severe). */
|
|
29
34
|
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low'] as const
|
|
@@ -563,6 +568,66 @@ export function renderRunReportSarif(
|
|
|
563
568
|
}
|
|
564
569
|
}
|
|
565
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Render the complete normalized run model as a JSON document.
|
|
573
|
+
*
|
|
574
|
+
* This is the lossless format: unlike Markdown (human projection) and SARIF
|
|
575
|
+
* (findings-only code-scanning log), the JSON carries EVERY fact the run
|
|
576
|
+
* recorded — findings, fixes, validations, convergence, the quality gate, the
|
|
577
|
+
* experience bank, and defense events — as structured data. CI dashboards and
|
|
578
|
+
* PR bots can consume it without re-parsing a human report. A missing or
|
|
579
|
+
* partial transcript still yields a valid document (empty arrays, null stores),
|
|
580
|
+
* so an export never fails because a run was interrupted.
|
|
581
|
+
*/
|
|
582
|
+
export interface RunReportJson {
|
|
583
|
+
$schema: 'iterate-run-report/1'
|
|
584
|
+
project: string
|
|
585
|
+
goal: string
|
|
586
|
+
mode: string | null
|
|
587
|
+
taskMode: string | null
|
|
588
|
+
round: number
|
|
589
|
+
maxRounds: number
|
|
590
|
+
active: boolean
|
|
591
|
+
stoppedReason: string | null
|
|
592
|
+
updatedAt: string
|
|
593
|
+
convergence: number[]
|
|
594
|
+
severityCounts: { critical: number; high: number; medium: number; low: number }
|
|
595
|
+
findings: ReportFinding[]
|
|
596
|
+
fixes: ReportFix[]
|
|
597
|
+
validations: ReportValidation[]
|
|
598
|
+
qualityGate: Record<string, unknown> | null
|
|
599
|
+
experienceBank: Record<string, unknown> | null
|
|
600
|
+
defenseEvents: Record<string, unknown> | null
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Render a JSON document from the same normalized data. */
|
|
604
|
+
export function renderRunReportJson(
|
|
605
|
+
manifest: unknown,
|
|
606
|
+
extra?: { qualityGate?: unknown; experienceBank?: unknown; defenseEvents?: unknown } | null,
|
|
607
|
+
): RunReportJson {
|
|
608
|
+
const data = buildRunReportData(manifest, extra)
|
|
609
|
+
return {
|
|
610
|
+
$schema: 'iterate-run-report/1',
|
|
611
|
+
project: data.project,
|
|
612
|
+
goal: data.goal,
|
|
613
|
+
mode: data.mode,
|
|
614
|
+
taskMode: data.taskMode,
|
|
615
|
+
round: data.round,
|
|
616
|
+
maxRounds: data.maxRounds,
|
|
617
|
+
active: data.active,
|
|
618
|
+
stoppedReason: data.stoppedReason,
|
|
619
|
+
updatedAt: data.updatedAt,
|
|
620
|
+
convergence: data.convergence,
|
|
621
|
+
severityCounts: data.severityCounts,
|
|
622
|
+
findings: data.findings,
|
|
623
|
+
fixes: data.fixes,
|
|
624
|
+
validations: data.validations,
|
|
625
|
+
qualityGate: data.qualityGate,
|
|
626
|
+
experienceBank: data.experienceBank,
|
|
627
|
+
defenseEvents: data.defenseEvents,
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
566
631
|
/**
|
|
567
632
|
* Render a report in the requested format. Unknown formats fall back to
|
|
568
633
|
* Markdown (the tool's default).
|
|
@@ -572,8 +637,9 @@ export function renderRunReport(
|
|
|
572
637
|
manifest: unknown,
|
|
573
638
|
extra?: { qualityGate?: unknown; experienceBank?: unknown; defenseEvents?: unknown } | null,
|
|
574
639
|
opts?: ReportRenderOptions,
|
|
575
|
-
): string | SarifLog {
|
|
640
|
+
): string | SarifLog | RunReportJson {
|
|
576
641
|
if (format === 'sarif') return renderRunReportSarif(manifest, extra, opts)
|
|
642
|
+
if (format === 'json') return renderRunReportJson(manifest, extra)
|
|
577
643
|
return renderRunReportMarkdown(manifest, extra, opts)
|
|
578
644
|
}
|
|
579
645
|
|
|
@@ -582,7 +648,7 @@ export function renderRunReport(
|
|
|
582
648
|
* `iterate-report-2026-10-18T01-02-03-000Z.md`.
|
|
583
649
|
*/
|
|
584
650
|
export function runReportFilename(format: string, opts?: { now?: string }): string {
|
|
585
|
-
const ext = format === 'sarif' ? 'sarif' : 'md'
|
|
651
|
+
const ext = format === 'sarif' ? 'sarif' : format === 'json' ? 'json' : 'md'
|
|
586
652
|
const stamp =
|
|
587
653
|
opts?.now && opts.now ? opts.now.replace(/[:.]/g, '-') : new Date().toISOString().replace(/[:.]/g, '-')
|
|
588
654
|
return `iterate-report-${stamp}.${ext}`
|