Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61163,7 +61163,7 @@ async function draftSddMarkdown(agent, configuration, plan, prompt, validate) {
try {
value = await agent.query({
configuration, agentId: 'pre-branch-sdd-draft',
prompt: `${attempt === 0 ? contractPrompt : `${contractPrompt}\n\n${problem === 'size' ? `The previous response exceeded the ${maximum}-character draft limit.` : `The previous response failed the required SDD ${formatReason} contract.`} Regenerate a concise complete draft for this same owner and plan. Use the exact numbered canonical headings from the supplied template. Preserve all resolved requirements, architecture, documentation and acceptance. Do not change ownership or truncate a requirement.`}\n\n${sdd_draft_consistency_1.SDD_CONSISTENCY_PROMPT}`,
prompt: `${attempt === 0 ? contractPrompt : `${contractPrompt}\n\n${problem === 'size' ? `The previous response exceeded the ${maximum}-character draft limit.` : `The previous response failed the required SDD ${formatReason} contract.`} Regenerate a concise complete draft for this same owner and plan. Use the exact numbered canonical headings from the supplied template. Preserve all resolved requirements, architecture, documentation and acceptance. Do not change ownership or truncate a requirement.`}\n\n${sdd_draft_consistency_1.SDD_CONSISTENCY_PROMPT}\n\n${sdd_draft_consistency_1.SDD_DERIVED_TABLES_PROMPT}`,
options: { expectJson: true, schemaName: 'pre_branch_sdd_draft', schema },
});
}
Expand All @@ -61185,8 +61185,13 @@ async function draftSddMarkdown(agent, configuration, plan, prompt, validate) {
continue;
}
try {
await validate?.(markdown);
return markdown;
const assembled = (0, sdd_draft_consistency_1.renderSddDerivedTables)(markdown);
if (assembled.length > maximum) {
problem = 'size';
continue;
}
await validate?.(assembled);
return assembled;
}
catch (error) {
if (!(error instanceof pre_branch_sdd_1.SddDraftFormatFailure))
Expand Down Expand Up @@ -93609,7 +93614,8 @@ function replyRequestBodyDigest(body) {
"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.SDD_CONSISTENCY_PROMPT = void 0;
exports.SDD_DERIVED_TABLES_PROMPT = exports.SDD_CONSISTENCY_PROMPT = void 0;
exports.renderSddDerivedTables = renderSddDerivedTables;
exports.reservedSddCaseIds = reservedSddCaseIds;
exports.validateSddDraftConsistency = validateSddDraftConsistency;
const pre_branch_sdd_1 = __nccwpck_require__(34730);
Expand All @@ -93624,6 +93630,10 @@ Also in section 14 include one inventory table: Case ID | Area | Requirements |
Each row is one distinct executable case with a fully qualified ID such as capability-change-C01, one exact budget area, comma-separated requirement IDs and an observable scenario. Never use shorthand C01, ranges or redefine an ID found in any owning SDD. Reference retained baseline cases in prose using their full IDs; count only the new inventory.
In section 17 include one table: Requirement | Cases. List comma-separated full case IDs for every requirement. Its links must exactly match the case inventory in both directions, with no undefined or generic documentation checks.
In section 15 include one table: Path | Change. Change is update, reference or create. Include the selected SDD path itself with update for an existing SDD or create for a new/companion SDD. Use existing immutable-base files for update/reference. Only explicitly proposed new files may use create; never mark a missing current document as an existing artifact. Cite repository files as full repository-relative paths in backticks, or relative Markdown file links. Put every planned new file in this table. These tables are publication consistency checks, not runtime/security acceptance.`;
const BUDGET_SLOT = '{{SDD_CASE_BUDGET}}';
const TRACEABILITY_SLOT = '{{SDD_REQUIREMENT_CASES}}';
/** The provider writes definitions once; trusted code renders their two redundant projections. */
exports.SDD_DERIVED_TABLES_PROMPT = `Draft assembly: the consistency contract above describes the final document. In your raw Markdown, write the complete Requirement | Behavior table in section 4 and Case ID | Area | Requirements | Scenario inventory in section 14. Preserve every resolved minimum, per-area allocation and observable case as actual inventory rows, never ranges or summaries. Instead of independently writing the budget table, put ${BUDGET_SLOT} once on its own visible line in section 14. Instead of writing the reverse Requirement | Cases table, put ${TRACEABILITY_SLOT} once on its own visible line in section 17. Do not include either derived table yourself or put these slots in examples, fences or comments. Trusted code derives exact counts, Total and reciprocal links from your unchanged definitions, then runs every publication check. The compact character target is advisory: use more space when required to preserve the full resolved inventory and scope. The hard limit applies after assembly too.`;
function fail(reason) {
// Closed diagnostics: rejected Markdown and repository text never become repair instructions.
throw new pre_branch_sdd_1.SddDraftFormatFailure(reason, `The SDD draft violates its ${reason} consistency contract.`);
Expand Down Expand Up @@ -93661,14 +93671,17 @@ function visible(source) {
}).join('\n');
}
function clean(value) { return value.trim().replace(/^(?:\*\*|__|`)(.*?)(?:\*\*|__|`)$/u, '$1').trim(); }
function table(source, section, headers, reason) {
function section(source, number, reason) {
const sections = [...source.matchAll(/^## (\d+)\. [^\n]*$/gmu)];
const matches = sections.filter(heading => Number(heading[1]) === section);
const matches = sections.filter(heading => Number(heading[1]) === number);
if (matches.length !== 1)
return fail(reason);
const start = matches[0];
const end = sections.find(heading => heading.index > start.index)?.index ?? source.length;
const lines = source.slice(start.index + start[0].length, end).split('\n');
return source.slice(start.index + start[0].length, end);
}
function table(source, number, headers, reason) {
const lines = section(source, number, reason).split('\n');
const found = [];
for (let index = 0; index < lines.length; index++) {
if (!/^\s*\|/u.test(lines[index]))
Expand Down Expand Up @@ -93699,6 +93712,47 @@ function ids(value, pattern, reason) {
return fail(reason);
return values;
}
/** Never repairs definitions or contradictory supplied tables; the writer independently revalidates. */
function renderSddDerivedTables(markdown) {
if (!markdown.includes(BUDGET_SLOT) && !markdown.includes(TRACEABILITY_SLOT))
return markdown;
const source = visible(markdown);
for (const [slot, number, reason] of [
[BUDGET_SLOT, 14, 'case-budget'], [TRACEABILITY_SLOT, 17, 'requirement-mapping'],
]) {
if (markdown.split(slot).length !== 2
|| section(source, number, reason).split('\n').filter(line => line.trim() === slot).length !== 1)
fail(reason);
}
const requirements = table(source, 4, ['Requirement', 'Behavior'], 'requirement-mapping').map(row => row[0]);
if (new Set(requirements).size !== requirements.length || requirements.some(id => !REQUIREMENT_ID.test(id)))
fail('requirement-mapping');
const cases = table(source, 14, ['Case ID', 'Area', 'Requirements', 'Scenario'], 'case-identity');
const caseIds = cases.map(row => row[0]);
if (new Set(caseIds).size !== caseIds.length || caseIds.some(id => !CASE_ID.test(id)))
fail('case-identity');
const counts = new Map();
const edges = new Map(requirements.map(id => [id, []]));
for (const [id, area, links] of cases) {
if (area === 'Total')
fail('case-budget');
counts.set(area, (counts.get(area) ?? 0) + 1);
for (const requirement of ids(links, REQUIREMENT_ID, 'requirement-mapping')) {
const linked = edges.get(requirement);
if (!linked)
fail('requirement-mapping');
linked.push(id);
}
}
if ([...edges.values()].some(links => links.length === 0))
fail('requirement-mapping');
const budget = ['| Area | Minimum distinct cases | Behaviors/risks covered |', '|---|---:|---|',
...[...counts].map(([area, count]) => `| ${area} | ${count} | Observable scenarios in the case inventory. |`),
`| Total | ${cases.length} | Distinct inventory cases; repeated executions do not add cases. |`].join('\n');
const traceability = ['| Requirement | Cases |', '|---|---|',
...[...edges].map(([requirement, links]) => `| ${requirement} | ${links.join(', ')} |`)].join('\n');
return markdown.replace(BUDGET_SLOT, () => budget).replace(TRACEABILITY_SLOT, () => traceability);
}
function reservedSddCaseIds(documents) {
const reserved = new Set();
for (const document of documents) {
Expand Down
68 changes: 61 additions & 7 deletions build/github_action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59163,7 +59163,7 @@ async function draftSddMarkdown(agent, configuration, plan, prompt, validate) {
try {
value = await agent.query({
configuration, agentId: 'pre-branch-sdd-draft',
prompt: `${attempt === 0 ? contractPrompt : `${contractPrompt}\n\n${problem === 'size' ? `The previous response exceeded the ${maximum}-character draft limit.` : `The previous response failed the required SDD ${formatReason} contract.`} Regenerate a concise complete draft for this same owner and plan. Use the exact numbered canonical headings from the supplied template. Preserve all resolved requirements, architecture, documentation and acceptance. Do not change ownership or truncate a requirement.`}\n\n${sdd_draft_consistency_1.SDD_CONSISTENCY_PROMPT}`,
prompt: `${attempt === 0 ? contractPrompt : `${contractPrompt}\n\n${problem === 'size' ? `The previous response exceeded the ${maximum}-character draft limit.` : `The previous response failed the required SDD ${formatReason} contract.`} Regenerate a concise complete draft for this same owner and plan. Use the exact numbered canonical headings from the supplied template. Preserve all resolved requirements, architecture, documentation and acceptance. Do not change ownership or truncate a requirement.`}\n\n${sdd_draft_consistency_1.SDD_CONSISTENCY_PROMPT}\n\n${sdd_draft_consistency_1.SDD_DERIVED_TABLES_PROMPT}`,
options: { expectJson: true, schemaName: 'pre_branch_sdd_draft', schema },
});
}
Expand All @@ -59185,8 +59185,13 @@ async function draftSddMarkdown(agent, configuration, plan, prompt, validate) {
continue;
}
try {
await validate?.(markdown);
return markdown;
const assembled = (0, sdd_draft_consistency_1.renderSddDerivedTables)(markdown);
if (assembled.length > maximum) {
problem = 'size';
continue;
}
await validate?.(assembled);
return assembled;
}
catch (error) {
if (!(error instanceof pre_branch_sdd_1.SddDraftFormatFailure))
Expand Down Expand Up @@ -84053,7 +84058,8 @@ function replyRequestBodyDigest(body) {
"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.SDD_CONSISTENCY_PROMPT = void 0;
exports.SDD_DERIVED_TABLES_PROMPT = exports.SDD_CONSISTENCY_PROMPT = void 0;
exports.renderSddDerivedTables = renderSddDerivedTables;
exports.reservedSddCaseIds = reservedSddCaseIds;
exports.validateSddDraftConsistency = validateSddDraftConsistency;
const pre_branch_sdd_1 = __nccwpck_require__(34730);
Expand All @@ -84068,6 +84074,10 @@ Also in section 14 include one inventory table: Case ID | Area | Requirements |
Each row is one distinct executable case with a fully qualified ID such as capability-change-C01, one exact budget area, comma-separated requirement IDs and an observable scenario. Never use shorthand C01, ranges or redefine an ID found in any owning SDD. Reference retained baseline cases in prose using their full IDs; count only the new inventory.
In section 17 include one table: Requirement | Cases. List comma-separated full case IDs for every requirement. Its links must exactly match the case inventory in both directions, with no undefined or generic documentation checks.
In section 15 include one table: Path | Change. Change is update, reference or create. Include the selected SDD path itself with update for an existing SDD or create for a new/companion SDD. Use existing immutable-base files for update/reference. Only explicitly proposed new files may use create; never mark a missing current document as an existing artifact. Cite repository files as full repository-relative paths in backticks, or relative Markdown file links. Put every planned new file in this table. These tables are publication consistency checks, not runtime/security acceptance.`;
const BUDGET_SLOT = '{{SDD_CASE_BUDGET}}';
const TRACEABILITY_SLOT = '{{SDD_REQUIREMENT_CASES}}';
/** The provider writes definitions once; trusted code renders their two redundant projections. */
exports.SDD_DERIVED_TABLES_PROMPT = `Draft assembly: the consistency contract above describes the final document. In your raw Markdown, write the complete Requirement | Behavior table in section 4 and Case ID | Area | Requirements | Scenario inventory in section 14. Preserve every resolved minimum, per-area allocation and observable case as actual inventory rows, never ranges or summaries. Instead of independently writing the budget table, put ${BUDGET_SLOT} once on its own visible line in section 14. Instead of writing the reverse Requirement | Cases table, put ${TRACEABILITY_SLOT} once on its own visible line in section 17. Do not include either derived table yourself or put these slots in examples, fences or comments. Trusted code derives exact counts, Total and reciprocal links from your unchanged definitions, then runs every publication check. The compact character target is advisory: use more space when required to preserve the full resolved inventory and scope. The hard limit applies after assembly too.`;
function fail(reason) {
// Closed diagnostics: rejected Markdown and repository text never become repair instructions.
throw new pre_branch_sdd_1.SddDraftFormatFailure(reason, `The SDD draft violates its ${reason} consistency contract.`);
Expand Down Expand Up @@ -84105,14 +84115,17 @@ function visible(source) {
}).join('\n');
}
function clean(value) { return value.trim().replace(/^(?:\*\*|__|`)(.*?)(?:\*\*|__|`)$/u, '$1').trim(); }
function table(source, section, headers, reason) {
function section(source, number, reason) {
const sections = [...source.matchAll(/^## (\d+)\. [^\n]*$/gmu)];
const matches = sections.filter(heading => Number(heading[1]) === section);
const matches = sections.filter(heading => Number(heading[1]) === number);
if (matches.length !== 1)
return fail(reason);
const start = matches[0];
const end = sections.find(heading => heading.index > start.index)?.index ?? source.length;
const lines = source.slice(start.index + start[0].length, end).split('\n');
return source.slice(start.index + start[0].length, end);
}
function table(source, number, headers, reason) {
const lines = section(source, number, reason).split('\n');
const found = [];
for (let index = 0; index < lines.length; index++) {
if (!/^\s*\|/u.test(lines[index]))
Expand Down Expand Up @@ -84143,6 +84156,47 @@ function ids(value, pattern, reason) {
return fail(reason);
return values;
}
/** Never repairs definitions or contradictory supplied tables; the writer independently revalidates. */
function renderSddDerivedTables(markdown) {
if (!markdown.includes(BUDGET_SLOT) && !markdown.includes(TRACEABILITY_SLOT))
return markdown;
const source = visible(markdown);
for (const [slot, number, reason] of [
[BUDGET_SLOT, 14, 'case-budget'], [TRACEABILITY_SLOT, 17, 'requirement-mapping'],
]) {
if (markdown.split(slot).length !== 2
|| section(source, number, reason).split('\n').filter(line => line.trim() === slot).length !== 1)
fail(reason);
}
const requirements = table(source, 4, ['Requirement', 'Behavior'], 'requirement-mapping').map(row => row[0]);
if (new Set(requirements).size !== requirements.length || requirements.some(id => !REQUIREMENT_ID.test(id)))
fail('requirement-mapping');
const cases = table(source, 14, ['Case ID', 'Area', 'Requirements', 'Scenario'], 'case-identity');
const caseIds = cases.map(row => row[0]);
if (new Set(caseIds).size !== caseIds.length || caseIds.some(id => !CASE_ID.test(id)))
fail('case-identity');
const counts = new Map();
const edges = new Map(requirements.map(id => [id, []]));
for (const [id, area, links] of cases) {
if (area === 'Total')
fail('case-budget');
counts.set(area, (counts.get(area) ?? 0) + 1);
for (const requirement of ids(links, REQUIREMENT_ID, 'requirement-mapping')) {
const linked = edges.get(requirement);
if (!linked)
fail('requirement-mapping');
linked.push(id);
}
}
if ([...edges.values()].some(links => links.length === 0))
fail('requirement-mapping');
const budget = ['| Area | Minimum distinct cases | Behaviors/risks covered |', '|---|---:|---|',
...[...counts].map(([area, count]) => `| ${area} | ${count} | Observable scenarios in the case inventory. |`),
`| Total | ${cases.length} | Distinct inventory cases; repeated executions do not add cases. |`].join('\n');
const traceability = ['| Requirement | Cases |', '|---|---|',
...[...edges].map(([requirement, links]) => `| ${requirement} | ${links.join(', ')} |`)].join('\n');
return markdown.replace(BUDGET_SLOT, () => budget).replace(TRACEABILITY_SLOT, () => traceability);
}
function reservedSddCaseIds(documents) {
const reserved = new Set();
for (const document of documents) {
Expand Down
12 changes: 12 additions & 0 deletions docs/issues/pre-branch-sdds.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ requirements or change the acceptance limits below. Codex receives a compatible
closed JSON shape; Copilot enforces the complete original string-length schema
locally before any document reaches the writer.

The provider writes the requirements and explicit case inventory once. Copilot
calculates the final area counts, total and reciprocal requirement links before
validating the document. Every resolved acceptance minimum and scenario must
remain in the inventory; the compact target is advisory, and the hard character
limit also applies after the calculated tables are inserted.

Assembly never changes case IDs, scenarios, requirement edges, supplied tables
or paths to make a contradiction pass. Malformed definitions, colliding IDs,
uncovered requirements and invalid paths still block publication. Existing
complete drafts retain their bytes and checks. Structural consistency does not
establish completeness or runtime acceptance; human review remains required.

Copilot limits proposed update appendices to 18,000 characters and complete new
or companion specifications to 70,000. If a generated draft exceeds its limit,
it tries one concise regeneration for the same owner and answers. Missing
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vypdev/copilot",
"version": "3.10.4",
"version": "3.10.5",
"packageManager": "pnpm@10.12.4",
"pnpm": {
"overrides": {
Expand Down
Loading
Loading