Skip to content

Commit cde8d6c

Browse files
committed
feat: expose readiness criterion id summaries
1 parent da4f44b commit cde8d6c

15 files changed

Lines changed: 165 additions & 3 deletions

docs/brainstorms/2026-04-19-mainline-progress-reconciliation-and-post-m7-direction-requirements.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ This document re-baselines current mainline truth on 2026-04-19, records what ch
220220
- readiness now reports `provenance.runtimeProjectRootAligned`
221221
- hermetic runtime/API paths can now prove they are reading real repo evidence instead of silently implying local runtime-root alignment
222222
- overall status still remains `in_progress` / `no-go`.
223+
- The next bounded execution slice after that makes unmet prerequisite ids directly diffable:
224+
- readiness now reports `promotionCriteriaSatisfiedIds`
225+
- readiness now reports `promotionCriteriaUnsatisfiedIds`
226+
- current mainline unmet ids are `graph_backend_independent` and `vector_backend_independent`
227+
- overall status still remains `in_progress` / `no-go`.
223228
- Current expected outcome on `main` remains:
224229
- status `in_progress`
225230
- decision `no-go`

docs/diataxis/en/explanation/development-progress-dashboard.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,23 @@ Execution anchor:
297297
- decision stays `no-go`,
298298
- file snapshots still do not qualify as independent graph backend readiness.
299299

300+
## Latest Mainline Increment (2026-04-20 M8.14 Foundation Criterion-Id Summaries)
301+
302+
- Added derived criterion-id summaries so CI and operator tooling can diff prerequisite state without scanning the full criteria array:
303+
- `FoundationReadinessReport.promotionCriteriaSatisfiedIds`
304+
- `FoundationReadinessReport.promotionCriteriaUnsatisfiedIds`
305+
- Current mainline unmet criteria are now explicit at the top level:
306+
- `graph_backend_independent`
307+
- `vector_backend_independent`
308+
- Runtime/operator visibility stayed aligned:
309+
- `scripts/verify-foundation-readiness.js` now prints satisfied and unsatisfied criterion ids,
310+
- markdown readiness reports include both id lists inside `Promotion Criteria`,
311+
- `src/frontend/agent_workspace_runtime.js` persists `promotionCriteriaUnsatisfiedIds` and surfaces `unmet graph_backend_independent, vector_backend_independent` in the toolbar summary.
312+
- Guardrail remains unchanged on `main`:
313+
- status stays `in_progress`,
314+
- decision stays `no-go`,
315+
- derived id lists improve diffability only; they do not alter readiness semantics.
316+
300317
## Latest Mainline Increment (2026-04-20 M8.13 Foundation Readiness Provenance Evidence)
301318

302319
- Added machine-readable provenance so operator tooling can tell whether readiness is reading the runtime project root or falling back to real repo evidence:

docs/diataxis/en/explanation/foundation-reentry-readiness-checklist.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ Bundle note:
8383
- passed `5/7`
8484
- satisfied: `store_backend_evidence_present`, `query_backend_boundary_present`, `vector_backend_present`, `docs_aligned`, `readiness_verifier_present`
8585
- unsatisfied: `graph_backend_independent`, `vector_backend_independent`
86+
- Current expected derived criterion-id summaries on `main` are:
87+
- `promotionCriteriaSatisfiedIds = [store_backend_evidence_present, query_backend_boundary_present, vector_backend_present, docs_aligned, readiness_verifier_present]`
88+
- `promotionCriteriaUnsatisfiedIds = [graph_backend_independent, vector_backend_independent]`
8689
- Current expected provenance behavior is:
8790
- direct verifier/utility usage reports `runtimeProjectRootAligned = true`
8891
- hermetic runtime/API usage may report `runtimeProjectRootAligned = false` while preserving the same repo-truth readiness payload

docs/diataxis/zh/explanation/development-progress-dashboard.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,23 @@
297297
- 决策仍为 `no-go`
298298
- file snapshot 仍不构成独立 graph backend readiness。
299299

300+
## 主线最新增量(2026-04-20 M8.14 Foundation 条件 ID 摘要)
301+
302+
- 已新增派生条件 ID 摘要,CI 与执行侧不再需要扫描整段 criteria 数组才能比对 prerequisite 状态:
303+
- `FoundationReadinessReport.promotionCriteriaSatisfiedIds`
304+
- `FoundationReadinessReport.promotionCriteriaUnsatisfiedIds`
305+
- 当前主线未满足条件现在可直接读取:
306+
- `graph_backend_independent`
307+
- `vector_backend_independent`
308+
- 运行时/执行侧可见性保持对齐:
309+
- `scripts/verify-foundation-readiness.js` 现在会输出 satisfied / unsatisfied criterion ids,
310+
- readiness markdown 报告会在 `Promotion Criteria` 小节中同时写出两组 id,
311+
- `src/frontend/agent_workspace_runtime.js` 会保留 `promotionCriteriaUnsatisfiedIds`,并在工具栏摘要里显示 `unmet graph_backend_independent, vector_backend_independent`
312+
- `main` 上的护栏结论保持不变:
313+
- 状态仍为 `in_progress`
314+
- 决策仍为 `no-go`
315+
- 派生 id 列表只提升可比对性,不改变 readiness 语义。
316+
300317
## 主线最新增量(2026-04-20 M8.13 Foundation Readiness 来源证据)
301318

302319
- 已新增 machine-readable provenance,执行侧现在可以直接知道 readiness 是读取 runtime project root,还是回退到真实 repo 证据:

docs/diataxis/zh/explanation/foundation-reentry-readiness-checklist.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
- 通过 `5/7`
8484
- 已满足:`store_backend_evidence_present``query_backend_boundary_present``vector_backend_present``docs_aligned``readiness_verifier_present`
8585
- 未满足:`graph_backend_independent``vector_backend_independent`
86+
- 当前 `main` 的预期派生条件 ID 摘要为:
87+
- `promotionCriteriaSatisfiedIds = [store_backend_evidence_present, query_backend_boundary_present, vector_backend_present, docs_aligned, readiness_verifier_present]`
88+
- `promotionCriteriaUnsatisfiedIds = [graph_backend_independent, vector_backend_independent]`
8689
- 当前预期 provenance 行为为:
8790
- 直接 verifier/utility 调用会报告 `runtimeProjectRootAligned = true`
8891
- hermetic runtime/API 调用可能报告 `runtimeProjectRootAligned = false`,但 readiness 载荷仍必须保持同一份 repo-truth 结论

scripts/foundation-readiness-utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ function evaluateFoundationReadiness(options = {}) {
336336
];
337337
const promotionCriteriaPassed = promotionCriteria.filter((entry) => entry.satisfied).length;
338338
const promotionCriteriaTotal = promotionCriteria.length;
339+
const promotionCriteriaSatisfiedIds = promotionCriteria
340+
.filter((entry) => entry.satisfied)
341+
.map((entry) => entry.criterionId);
342+
const promotionCriteriaUnsatisfiedIds = promotionCriteria
343+
.filter((entry) => !entry.satisfied)
344+
.map((entry) => entry.criterionId);
339345

340346
const promotionBlockers = [];
341347
const recommendations = [];
@@ -480,6 +486,8 @@ function evaluateFoundationReadiness(options = {}) {
480486
mandatoryChecks,
481487
promotionCriteriaPassed,
482488
promotionCriteriaTotal,
489+
promotionCriteriaSatisfiedIds,
490+
promotionCriteriaUnsatisfiedIds,
483491
promotionCriteria,
484492
promotionBlockers,
485493
recommendations,
@@ -545,6 +553,8 @@ function formatFoundationReadinessMarkdown(result, reportPaths) {
545553

546554
lines.push('', '## Promotion Criteria', '');
547555
lines.push(`- Passed: ${result.promotionCriteriaPassed}/${result.promotionCriteriaTotal}`);
556+
lines.push(`- Satisfied ids: ${result.promotionCriteriaSatisfiedIds.join(', ') || 'none'}`);
557+
lines.push(`- Unsatisfied ids: ${result.promotionCriteriaUnsatisfiedIds.join(', ') || 'none'}`);
548558
result.promotionCriteria.forEach((entry) => {
549559
lines.push(`- ${entry.criterionId}: ${entry.satisfied ? 'yes' : 'no'} (${entry.summary})`);
550560
});

scripts/verify-foundation-readiness.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ function printHumanReport(result, reportPaths) {
5656
console.log(
5757
`[Foundation Readiness] Promotion criteria: ${result.promotionCriteriaPassed}/${result.promotionCriteriaTotal}`
5858
);
59+
console.log(
60+
`[Foundation Readiness] Promotion criteria satisfied ids: ${result.promotionCriteriaSatisfiedIds.join(', ') || 'none'}`
61+
);
62+
console.log(
63+
`[Foundation Readiness] Promotion criteria unsatisfied ids: ${result.promotionCriteriaUnsatisfiedIds.join(', ') || 'none'}`
64+
);
5965
result.promotionCriteria.forEach((entry) => {
6066
console.log(
6167
` - ${entry.criterionId}: ${entry.satisfied ? 'yes' : 'no'} (${entry.summary})`

src/agent_workspace.runtime.behavior.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,17 @@ describe('agent workspace runtime behavior', () => {
16941694
},
16951695
promotionCriteriaPassed: 5,
16961696
promotionCriteriaTotal: 7,
1697+
promotionCriteriaSatisfiedIds: [
1698+
'store_backend_evidence_present',
1699+
'query_backend_boundary_present',
1700+
'vector_backend_present',
1701+
'docs_aligned',
1702+
'readiness_verifier_present',
1703+
],
1704+
promotionCriteriaUnsatisfiedIds: [
1705+
'graph_backend_independent',
1706+
'vector_backend_independent',
1707+
],
16971708
promotionCriteria: [
16981709
{
16991710
criterionId: 'store_backend_evidence_present',
@@ -1779,6 +1790,7 @@ describe('agent workspace runtime behavior', () => {
17791790
expect(messages.textContent || '').toContain('lexical_token_overlap');
17801791
expect(messages.textContent || '').toContain('repo-source cwd');
17811792
expect(messages.textContent || '').toContain('aligned no');
1793+
expect(messages.textContent || '').toContain('unmet graph_backend_independent, vector_backend_independent');
17821794
expect(messages.textContent || '').toContain('graph_backend_not_independent');
17831795
expect(messages.textContent || '').toContain('vector_backend_not_independent');
17841796
expect(messages.textContent || '').toContain('criteria 5/7');
@@ -1805,6 +1817,10 @@ describe('agent workspace runtime behavior', () => {
18051817
vectorAdapterLinkedIntoQueryBackend: true,
18061818
repoRootSource: 'cwd',
18071819
runtimeProjectRootAligned: false,
1820+
promotionCriteriaUnsatisfiedIds: [
1821+
'graph_backend_independent',
1822+
'vector_backend_independent',
1823+
],
18081824
promotionBlockerIds: [
18091825
'graph_backend_not_independent',
18101826
'vector_backend_not_independent',

src/foundation.readiness.contract.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ type FoundationReadinessResult = {
3939
};
4040
promotionCriteriaPassed: number;
4141
promotionCriteriaTotal: number;
42+
promotionCriteriaSatisfiedIds: Array<
43+
| 'store_backend_evidence_present'
44+
| 'graph_backend_independent'
45+
| 'query_backend_boundary_present'
46+
| 'vector_backend_present'
47+
| 'vector_backend_independent'
48+
| 'docs_aligned'
49+
| 'readiness_verifier_present'
50+
>;
51+
promotionCriteriaUnsatisfiedIds: Array<
52+
| 'store_backend_evidence_present'
53+
| 'graph_backend_independent'
54+
| 'query_backend_boundary_present'
55+
| 'vector_backend_present'
56+
| 'vector_backend_independent'
57+
| 'docs_aligned'
58+
| 'readiness_verifier_present'
59+
>;
4260
promotionCriteria: Array<{
4361
criterionId:
4462
| 'store_backend_evidence_present'
@@ -168,6 +186,17 @@ describe('foundation readiness contract', () => {
168186
});
169187
expect(result.promotionCriteriaPassed).toBe(5);
170188
expect(result.promotionCriteriaTotal).toBe(7);
189+
expect(result.promotionCriteriaSatisfiedIds).toEqual([
190+
'store_backend_evidence_present',
191+
'query_backend_boundary_present',
192+
'vector_backend_present',
193+
'docs_aligned',
194+
'readiness_verifier_present',
195+
]);
196+
expect(result.promotionCriteriaUnsatisfiedIds).toEqual([
197+
'graph_backend_independent',
198+
'vector_backend_independent',
199+
]);
171200
expect(result.promotionCriteria.map((entry) => entry.criterionId)).toEqual([
172201
'store_backend_evidence_present',
173202
'graph_backend_independent',
@@ -265,6 +294,17 @@ describe('foundation readiness contract', () => {
265294
});
266295
expect(result.promotionCriteriaPassed).toBe(6);
267296
expect(result.promotionCriteriaTotal).toBe(7);
297+
expect(result.promotionCriteriaSatisfiedIds).toEqual([
298+
'store_backend_evidence_present',
299+
'query_backend_boundary_present',
300+
'vector_backend_present',
301+
'vector_backend_independent',
302+
'docs_aligned',
303+
'readiness_verifier_present',
304+
]);
305+
expect(result.promotionCriteriaUnsatisfiedIds).toEqual([
306+
'graph_backend_independent',
307+
]);
268308
expect(
269309
result.promotionCriteria
270310
.filter((entry) => entry.satisfied)
@@ -350,6 +390,16 @@ describe('foundation readiness contract', () => {
350390
});
351391
expect(result.promotionCriteriaPassed).toBe(7);
352392
expect(result.promotionCriteriaTotal).toBe(7);
393+
expect(result.promotionCriteriaSatisfiedIds).toEqual([
394+
'store_backend_evidence_present',
395+
'graph_backend_independent',
396+
'query_backend_boundary_present',
397+
'vector_backend_present',
398+
'vector_backend_independent',
399+
'docs_aligned',
400+
'readiness_verifier_present',
401+
]);
402+
expect(result.promotionCriteriaUnsatisfiedIds).toEqual([]);
353403
expect(result.promotionCriteria.every((entry) => entry.satisfied)).toBe(true);
354404
expect(result.promotionBlockers).toEqual([]);
355405
});
@@ -401,6 +451,17 @@ describe('foundation readiness contract', () => {
401451
});
402452
expect(result.promotionCriteriaPassed).toBe(3);
403453
expect(result.promotionCriteriaTotal).toBe(7);
454+
expect(result.promotionCriteriaSatisfiedIds).toEqual([
455+
'store_backend_evidence_present',
456+
'docs_aligned',
457+
'readiness_verifier_present',
458+
]);
459+
expect(result.promotionCriteriaUnsatisfiedIds).toEqual([
460+
'graph_backend_independent',
461+
'query_backend_boundary_present',
462+
'vector_backend_present',
463+
'vector_backend_independent',
464+
]);
404465
expect(
405466
result.promotionCriteria
406467
.filter((entry) => entry.satisfied)

src/frontend/agent_workspace_runtime.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,7 @@
14461446
runtimeProjectRootAligned: false,
14471447
promotionCriteriaPassed: 0,
14481448
promotionCriteriaTotal: 0,
1449+
promotionCriteriaUnsatisfiedIds: [],
14491450
promotionBlockerIds: [],
14501451
promotionBlockersCount: 0,
14511452
recommendationsCount: 0,
@@ -1499,6 +1500,9 @@
14991500
const promotionCriteriaTotal = Number.isFinite(Number(readiness && readiness.promotionCriteriaTotal))
15001501
? Number(readiness && readiness.promotionCriteriaTotal)
15011502
: promotionCriteria.length;
1503+
const promotionCriteriaUnsatisfiedIds = Array.isArray(readiness && readiness.promotionCriteriaUnsatisfiedIds)
1504+
? readiness.promotionCriteriaUnsatisfiedIds.map((entry) => trimString(entry)).filter(Boolean)
1505+
: promotionCriteria.filter((entry) => !entry.satisfied).map((entry) => entry.criterionId);
15021506
diagnostics.lastFoundationReadiness = {
15031507
requestedAt,
15041508
respondedAt: nowIso(),
@@ -1521,6 +1525,7 @@
15211525
runtimeProjectRootAligned: Boolean(provenance.runtimeProjectRootAligned),
15221526
promotionCriteriaPassed,
15231527
promotionCriteriaTotal,
1528+
promotionCriteriaUnsatisfiedIds,
15241529
promotionBlockerIds,
15251530
promotionBlockersCount: promotionBlockerIds.length,
15261531
recommendationsCount,
@@ -1532,7 +1537,7 @@
15321537
'assistant',
15331538
getI18nText(
15341539
'agentWorkspace.messages.foundationReadinessLoaded',
1535-
`Foundation readiness ${trimString(readiness && readiness.status) || 'unknown'} / ${trimString(readiness && readiness.decision) || 'unknown'} (store ${trimString(baseline.storeType) || 'n/a'}, graph ${trimString(baseline.graphBackendStatus) || 'n/a'}/${trimString(baseline.graphBackendSignalKind) || 'n/a'}, graph-independent ${Boolean(baseline.graphBackendIndependent) ? 'yes' : 'no'}, backend ${trimString(baseline.queryBackendDefaultMode) || 'n/a'}, signals ${queryBackendScoreSignals.join(', ') || 'none'}, vector ${trimString(baseline.vectorAdapterStatus) || 'n/a'}, signal-kind ${trimString(baseline.vectorAdapterSignalKind) || 'n/a'}, independent ${Boolean(baseline.vectorAdapterIndependent) ? 'yes' : 'no'}, linked ${Boolean(baseline.vectorAdapterLinkedIntoQueryBackend) ? 'yes' : 'no'}, repo-source ${trimString(provenance.repoRootSource) || 'n/a'}, aligned ${Boolean(provenance.runtimeProjectRootAligned) ? 'yes' : 'no'}, criteria ${promotionCriteriaPassed}/${promotionCriteriaTotal}, blockers ${promotionBlockerIds.join(', ') || 'none'}).`,
1540+
`Foundation readiness ${trimString(readiness && readiness.status) || 'unknown'} / ${trimString(readiness && readiness.decision) || 'unknown'} (store ${trimString(baseline.storeType) || 'n/a'}, graph ${trimString(baseline.graphBackendStatus) || 'n/a'}/${trimString(baseline.graphBackendSignalKind) || 'n/a'}, graph-independent ${Boolean(baseline.graphBackendIndependent) ? 'yes' : 'no'}, backend ${trimString(baseline.queryBackendDefaultMode) || 'n/a'}, signals ${queryBackendScoreSignals.join(', ') || 'none'}, vector ${trimString(baseline.vectorAdapterStatus) || 'n/a'}, signal-kind ${trimString(baseline.vectorAdapterSignalKind) || 'n/a'}, independent ${Boolean(baseline.vectorAdapterIndependent) ? 'yes' : 'no'}, linked ${Boolean(baseline.vectorAdapterLinkedIntoQueryBackend) ? 'yes' : 'no'}, repo-source ${trimString(provenance.repoRootSource) || 'n/a'}, aligned ${Boolean(provenance.runtimeProjectRootAligned) ? 'yes' : 'no'}, criteria ${promotionCriteriaPassed}/${promotionCriteriaTotal}, unmet ${promotionCriteriaUnsatisfiedIds.join(', ') || 'none'}, blockers ${promotionBlockerIds.join(', ') || 'none'}).`,
15361541
{
15371542
status: trimString(readiness && readiness.status) || 'unknown',
15381543
decision: trimString(readiness && readiness.decision) || 'unknown',
@@ -1552,6 +1557,7 @@
15521557
runtimeProjectRootAligned: Boolean(provenance.runtimeProjectRootAligned) ? 'yes' : 'no',
15531558
promotionCriteriaPassed,
15541559
promotionCriteriaTotal,
1560+
promotionCriteriaUnsatisfiedIds: promotionCriteriaUnsatisfiedIds.join(', ') || 'none',
15551561
promotionBlockerIds: promotionBlockerIds.join(', ') || 'none',
15561562
}
15571563
)
@@ -1577,6 +1583,7 @@
15771583
runtimeProjectRootAligned: false,
15781584
promotionCriteriaPassed: 0,
15791585
promotionCriteriaTotal: 0,
1586+
promotionCriteriaUnsatisfiedIds: [],
15801587
promotionBlockerIds: [],
15811588
promotionBlockersCount: 0,
15821589
recommendationsCount: 0,

0 commit comments

Comments
 (0)