Skip to content

Commit 19a49b4

Browse files
V29 Gate 10: Close promoted parity preparation
Teach the V29 promotion preparer to rewrite promoted implementation matrix and checklist judgments before promoted-mode spec-family validation. Tighten the Gate 10 checker, document the promoted parity requirement, and add demonstration coverage for status plus parity mutation.
1 parent 415146f commit 19a49b4

5 files changed

Lines changed: 133 additions & 5 deletions

BITCODE_SPEC_V29_DELTA.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ Closure acceptance:
216216
pull requests into `main`, validates the source branch, runs promotion-grade
217217
proof, and commits the generated V29 promotion artifacts back to `version/v29`.
218218
- `prepare-bitcode-spec-family-promotion.mjs` rewrites V29 hand-authored status
219-
truth for promoted mode.
219+
truth and the promoted V29 implementation matrix/checklist judgments for
220+
promoted mode.
220221
- `prepare-bitcode-runtime-canon-promotion.mjs` rewrites both standalone
221222
demonstration posture and commercial `packages/protocol` posture for V29
222223
active / V30 draft.

BITCODE_SPEC_V29_PARITY_MATRIX.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ Accepted surfaces:
325325
Terminal browser proof, runtime/spec preparation, generated proof creation,
326326
and promoted-family validation.
327327
- `scripts/prepare-bitcode-spec-family-promotion.mjs` rewrites V29
328-
hand-authored status truth for promoted mode.
328+
hand-authored status truth and the promoted V29 implementation
329+
matrix/checklist judgments for promoted mode.
329330
- `scripts/prepare-bitcode-runtime-canon-promotion.mjs` rewrites standalone
330331
demonstration posture and commercial `packages/protocol` posture/data for V29
331332
active / V30 draft.

protocol-demonstration/test/v22-canon-drift.test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,80 @@ test('runtime promotion preparation rewrites optional protocol package posture c
460460
assert.match(rewrittenPackageState, /"inheritedCanonSurfaceLabel": "V16\/V17\/V18\/V19\/V20\/V21\/V22\/V23\/V24\/V25\/V26\/V27\/V28"/);
461461
});
462462

463+
test('V29 spec-family promotion preparation rewrites status truth and promoted parity judgments', async () => {
464+
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bitcode-spec-family-v29-'));
465+
const status = [
466+
'## Status',
467+
'',
468+
'- Current canonical/latest target: `V28`',
469+
'- Source parity state: draft',
470+
'- V29 state: draft implementation underway'
471+
].join('\n');
472+
const parity = [
473+
'# Bitcode Spec V29 Parity Matrix',
474+
'',
475+
status,
476+
'',
477+
'## V29 implementation matrix',
478+
'',
479+
'| Area | Gate | Source evidence | Judgment | Closure requirement |',
480+
'| --- | --- | --- | --- | --- |',
481+
'| Promotion readiness | Gate 10 | promotion workflow | drafted | Generated canon can be committed. |',
482+
'',
483+
'## V29 implementation checklist',
484+
'',
485+
'| Area | Required V29 result | Judgment |',
486+
'| --- | --- | --- |',
487+
'| Gate 10 promotion readiness | promotion dry-run and generated-canon automation | drafted |',
488+
'',
489+
'## Other table',
490+
'',
491+
'| Area | Judgment |',
492+
'| --- | --- |',
493+
'| Draft-only audit | drafted |'
494+
].join('\n');
495+
/** @type {Array<[string, string]>} */
496+
const files = [
497+
['BITCODE_SPEC_V29.md', `# Bitcode Spec V29\n\n${status}\n\n## Body\nx\n`],
498+
['BITCODE_SPEC_V29_DELTA.md', `# Bitcode Spec V29 Delta\n\n${status}\n\n## Body\nx\n`],
499+
['BITCODE_SPEC_V29_NOTES.md', `# Bitcode Spec V29 Notes\n\n${status}\n\n## Body\nx\n`],
500+
['BITCODE_SPEC_V29_PARITY_MATRIX.md', parity]
501+
];
502+
await Promise.all(files.map(([relativePath, content]) => fs.writeFile(path.join(fixtureRoot, relativePath), content, 'utf8')));
503+
504+
const output = execFileSync(process.execPath, [
505+
prepareSpecFamilyScriptPath,
506+
'--version',
507+
'V29',
508+
'--commit',
509+
'deadbeef',
510+
'--repo-root',
511+
fixtureRoot
512+
], {
513+
cwd: fixtureRoot,
514+
encoding: 'utf8'
515+
});
516+
517+
assert.match(output, /Prepared V29 hand-authored spec family for promotion with proof-source commit deadbeef/);
518+
519+
for (const relativePath of files.map(([relativePath]) => relativePath)) {
520+
const rewritten = await fs.readFile(path.join(fixtureRoot, relativePath), 'utf8');
521+
assert.match(rewritten, /Current canonical\/latest target: `V29`/);
522+
assert.match(rewritten, /Canonical proof-source commit: `deadbeef`/);
523+
const versionStateLine = rewritten.match(/^- V29 state: (.+)$/m);
524+
const stateValue = versionStateLine?.[1];
525+
if (typeof stateValue !== 'string') {
526+
throw new Error(`Missing V29 state line in ${relativePath}`);
527+
}
528+
assert.doesNotMatch(stateValue, /draft|pending|in progress/i);
529+
}
530+
531+
const rewrittenParity = await fs.readFile(path.join(fixtureRoot, 'BITCODE_SPEC_V29_PARITY_MATRIX.md'), 'utf8');
532+
assert.match(rewrittenParity, /\| Promotion readiness \| Gate 10 \| promotion workflow \| closed \|/);
533+
assert.match(rewrittenParity, /\| Gate 10 promotion readiness \| promotion dry-run and generated-canon automation \| closed \|/);
534+
assert.match(rewrittenParity, /\| Draft-only audit \| drafted \|/);
535+
});
536+
463537
test('V25 promotion dry-run includes drift detection and runtime posture preparation', () => {
464538
const output = execFileSync(process.execPath, [
465539
promoteScriptPath,

scripts/check-v29-gate10-local-staging-promotion-readiness.mjs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,11 @@ function main() {
178178
failures,
179179
prepareSpecScript.includes("if (version === 'V29')") &&
180180
prepareSpecScript.includes('V29 canonical system specification for Terminal transaction depth') &&
181-
prepareSpecScript.includes('BITCODE_SPEC_V29_PROVEN.md'),
182-
'Spec-family promotion preparation must rewrite V29 hand-authored status truth.',
181+
prepareSpecScript.includes('BITCODE_SPEC_V29_PROVEN.md') &&
182+
prepareSpecScript.includes('rewritePromotedParityJudgments') &&
183+
prepareSpecScript.includes('implementation matrix') &&
184+
prepareSpecScript.includes('implementation checklist'),
185+
'Spec-family promotion preparation must rewrite V29 hand-authored status truth and promoted parity judgments.',
183186
);
184187
assertCheck(
185188
failures,

scripts/prepare-bitcode-spec-family-promotion.mjs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,54 @@ function rewriteStatusValues(content, values) {
9797
return `${content.slice(0, section.start)}${section.prefix}${body}${content.slice(section.end)}`;
9898
}
9999

100+
/**
101+
* @param {string} content
102+
* @param {string} heading
103+
* @returns {string}
104+
*/
105+
function rewritePromotedParityTableJudgments(content, heading) {
106+
const headingPattern = new RegExp(`^## ${heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*$`, 'm');
107+
const headingMatch = content.match(headingPattern);
108+
if (!headingMatch || typeof headingMatch.index !== 'number') return content;
109+
110+
const sectionStart = headingMatch.index;
111+
const nextHeadingMatch = content.slice(sectionStart + headingMatch[0].length).match(/^##\s/m);
112+
const sectionEnd = nextHeadingMatch
113+
? sectionStart + headingMatch[0].length + nextHeadingMatch.index
114+
: content.length;
115+
const section = content.slice(sectionStart, sectionEnd);
116+
const lines = section.split('\n');
117+
const headerIndex = lines.findIndex((line) => line.trim().startsWith('|'));
118+
if (headerIndex < 0) return content;
119+
120+
const headers = lines[headerIndex].split('|').slice(1, -1).map((cell) => cell.trim());
121+
const judgmentIndex = headers.findIndex((header) => header === 'Judgment');
122+
if (judgmentIndex < 0) return content;
123+
124+
const rewrittenLines = lines.map((line, index) => {
125+
if (index <= headerIndex + 1 || !line.trim().startsWith('|')) return line;
126+
const cells = line.split('|');
127+
const cellIndex = judgmentIndex + 1;
128+
if (!cells[cellIndex] || cells[cellIndex].trim() !== 'drafted') return line;
129+
cells[cellIndex] = ' closed ';
130+
return cells.join('|');
131+
});
132+
133+
return `${content.slice(0, sectionStart)}${rewrittenLines.join('\n')}${content.slice(sectionEnd)}`;
134+
}
135+
136+
/**
137+
* @param {string} content
138+
* @param {string} version
139+
* @returns {string}
140+
*/
141+
function rewritePromotedParityJudgments(content, version) {
142+
return [
143+
`${version} implementation matrix`,
144+
`${version} implementation checklist`
145+
].reduce((rewritten, heading) => rewritePromotedParityTableJudgments(rewritten, heading), content);
146+
}
147+
100148
/**
101149
* @param {string} version
102150
* @param {string} commit
@@ -146,7 +194,7 @@ function rewritePromotionStatus(version, commit, content, kind) {
146194
notes: 'canonical promotion complete; V29 notes record the accepted Terminal-depth, local/staging, and promotion-readiness evidence',
147195
parity: 'canonical promotion complete; V29 parity truth, product-gate audit, generated proof, and promotion automation are aligned'
148196
};
149-
return rewriteStatusValues(content, {
197+
const rewritten = rewriteStatusValues(content, {
150198
Scope: scopeByKind[kind],
151199
...(kind !== 'delta'
152200
? { 'Last fully realized canonical target preserved in source': '`V29`' }
@@ -158,6 +206,7 @@ function rewritePromotionStatus(version, commit, content, kind) {
158206
'V29 source-side Terminal transaction, wallet/BTC, Reading observability, AssetPack disclosure, settlement repair, organization authority, UX proof, workflow, and promotion surfaces are canonicalized in the promoted V29 file family',
159207
'V29 state': stateByKind[kind]
160208
});
209+
return kind === 'parity' ? rewritePromotedParityJudgments(rewritten, version) : rewritten;
161210
}
162211

163212
if (!['V21', 'V22', 'V23', 'V24', 'V25'].includes(version)) {

0 commit comments

Comments
 (0)