Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
/**
* Sync-obligations ledger (design D13). Deterministic post-tool-call hooks
* record obligations; the commit gate refuses while any is open. This is what
* turns "keep everything in sync" into a mechanical gate instead of a prompt.
*/
export type ObligationKind =
| 'erc'
| 'drc'
| 'drift'
| 'changelog'
| 'decision-log'
| 'constraint-dual-write'
| 'affects-revisit';
export interface Obligation {
kind: ObligationKind;
detail: string;
openedBy: string;
}
export class ObligationsLedger {
private open: Obligation[] = [];
add(kind: ObligationKind, detail: string, openedBy: string): void {
if (!this.open.some((o) => o.kind === kind && o.detail === detail)) {
this.open.push({ kind, detail, openedBy });
}
}
/** Returns true if at least one obligation was actually removed. */
clear(kind: ObligationKind, detail?: string): boolean {
const before = this.open.length;
this.open = this.open.filter(
(o) => !(o.kind === kind && (detail === undefined || o.detail === detail)),
);
return this.open.length < before;
}
/** Open obligations of one kind, for building corrective tool errors. */
openOfKind(kind: ObligationKind): readonly Obligation[] {
return this.open.filter((o) => o.kind === kind);
}
/** A KiCad edit re-opens verification obligations even if previously cleared. */
onKicadEdit(file: string): void {
this.add('erc', 'ERC must pass after schematic edits', file);
if (file.endsWith('.kicad_pcb')) this.add('drc', 'DRC must pass after board edits', file);
this.add('drift', 'check_drift must run clean after KiCad edits', file);
this.add('changelog', 'CHANGELOG.md entry for this run', file);
}
onDocEdit(file: string): void {
this.add('drift', 'check_drift must run clean after doc edits', file);
}
onConstraintChange(constraintKey: string, affects: string[]): void {
this.add('constraint-dual-write', constraintKey, constraintKey);
for (const item of affects) {
this.add('affects-revisit', `${constraintKey} affects ${item}`, constraintKey);
}
}
onDecision(summary: string): void {
this.add('decision-log', summary, 'decision');
}
get openObligations(): readonly Obligation[] {
return this.open;
}
get isClear(): boolean {
return this.open.length === 0;
}
describe(): string {
if (this.isClear) return 'all sync obligations satisfied';
return this.open.map((o) => `- [${o.kind}] ${o.detail} (opened by ${o.openedBy})`).join('\n');
}
}
7f2688d19This file is pinned to the selected release. Download the original to open it in your design tool.