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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"verify:schemas": "node scripts/generate-response-schemas.mjs --check",
"verify:structured-responses": "node scripts/verify-structured-responses.mjs",
"schemas:generate": "npm run build && node scripts/generate-response-schemas.mjs",
"verify": "npm run check && npm run verify:no-llm && npm run verify:modules && npm run verify:env && npm run verify:workflows && npm run verify:generated-analysis && npm run verify:structured-responses && npm run build && npm run verify:schemas && npm test",
"verify": "npm run check && npm run verify:no-llm && npm run verify:modules && npm run verify:env && npm run verify:workflows && npm run verify:generated-analysis && npm run verify:structured-responses && npm run build && npm run verify:schemas && npm run verify:merge-conflict-contract && npm test",
"verify:merge-conflict-contract": "node scripts/verify-merge-conflict-contract.mjs",
"start": "node dist/src/cli.js",
"mcp": "node dist/src/interfaces/mcp.js",
"a2a": "node dist/src/interfaces/a2a.js",
Expand Down
31 changes: 31 additions & 0 deletions project/ticket-105/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions project/ticket-105/intent.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions scripts/verify-merge-conflict-contract.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { promises as fs } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { extractMergeConflicts } from '../dist/src/extractors/merge-conflicts.js';
import { assertIntentRecord } from '../dist/src/core/schema.js';

// Check the actual wire projection, independently of the compile-only public
// type. No LLM, network, repository state or merge operation is involved.
const root = await fs.mkdtemp(path.join(os.tmpdir(), 't2c-conflict-contract-'));
try {
let checked = 0;
for (const style of ['merge', 'diff3']) {
for (const eol of ['\n', '\r\n']) {
const lines = ['<<<<<<< ours', 'x'.repeat(2100),
...(style === 'diff3' ? ['||||||| base', 'base'] : []),
'=======', 'theirs', '>>>>>>> theirs'];
const body = lines.join(eol);
await fs.writeFile(path.join(root, 'conflict.toml'), body);
const result = await extractMergeConflicts(root, { maxFileBytes: 10_000 });
assert.equal(result.warnings.length, 0);
assert.equal(result.records.length, 1);
const record = JSON.parse(JSON.stringify(result.records[0]));
assertIntentRecord(record);
assert.equal(record.schemaVersion, 't2c.intent/v1');
assert.equal(record.statement.kind, 'merge_conflict_fact');
assert.equal(record.statement.action, 'block');
assert.equal(record.statement.modality, 'observed');
assert.equal(record.lifecycle.status, 'blocked');
assert.equal(record.epistemic.class, 'fact');
assert.equal(record.source.kind, 'git');
assert.equal(record.source.path, 'conflict.toml');
assert.equal(record.source.extractor, 't2c/merge-conflict-markers@1');
assert.deepEqual(record.source.lines, { start: 1, end: lines.length });
assert.deepEqual(record.statement.target.paths, ['conflict.toml']);
assert.equal(record.metadata.llmUsed, false);
assert.equal(record.metadata.gitIndexVerified, false);
assert.equal(record.metadata.conflictStyle, style);
assert.equal(record.metadata.markerWidth, 7);
assert.equal(record.source.rawExcerpt, body.slice(0, 2000));
assert.equal(record.metadata.blockSha256, createHash('sha256').update(body).digest('hex'));
checked++;
}
}
console.log(`merge conflict data contract: PASS (${checked} real wire projections)`);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
31 changes: 31 additions & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
import type { IntentRecord } from './types-foundation.js';

export * from './types-foundation.js';
export * from './types-code-change.js';
export * from './types-runtime.js';

/**
* Additive view of observed conflict-marker syntax in t2c.intent/v1.
* This is neither Git-index verification nor authority to choose a merge side.
* blockSha256 binds the complete block; rawExcerpt may be truncated.
*/
export type MergeConflictFact = IntentRecord & {
statement: IntentRecord['statement'] & {
kind: 'merge_conflict_fact';
action: 'block';
modality: 'observed';
};
lifecycle: { status: 'blocked' };
source: IntentRecord['source'] & {
kind: 'git';
path: string;
lines: { start: number; end: number };
extractor: 't2c/merge-conflict-markers@1';
rawExcerpt: string;
};
epistemic: IntentRecord['epistemic'] & { class: 'fact' };
metadata: IntentRecord['metadata'] & {
llmUsed: false;
gitIndexVerified: false;
conflictStyle: 'merge' | 'diff3';
markerWidth: number;
blockSha256: string;
};
};
20 changes: 20 additions & 0 deletions test/fixtures/contracts/merge-conflict-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { IntentRecord, MergeConflictFact } from '../../../src/core/types.js';

// Compile-only consumers: the public view remains assignable to IntentRecord,
// but generic facts and authority claims cannot masquerade as marker evidence.
export function contract(fact: MergeConflictFact, generic: IntentRecord): IntentRecord {
const observed: 'observed' = fact.statement.modality;
const notVerified: false = fact.metadata.gitIndexVerified;
const source: string = fact.source.path;
void [observed, notVerified, source];
// @ts-expect-error a generic record has no conflict-fact guarantees
const missingContract: MergeConflictFact = generic;
// @ts-expect-error syntactic observations do not verify the Git index
const verified: MergeConflictFact['metadata'] = { ...fact.metadata, gitIndexVerified: true };
// @ts-expect-error marker evidence cannot authorize a merge
const approval: MergeConflictFact['statement'] = { ...fact.statement, action: 'approve' };
// @ts-expect-error the source must identify a file
const missingSource: MergeConflictFact['source'] = { ...fact.source, path: null };
void [missingContract, verified, approval, missingSource];
return fact;
}
Loading