mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 15:16:35 +03:00
Restore dice assignment, fusion, and the commitment counterweights
The ship40 concept pipeline had reversed the proven a-series mechanisms: the seed's roll decayed into a shortlist nomination that taste functions (model ranking, candidate floor, simulated user) then argmaxed into the safest card; the costume check returned as the Translation veto and carrier-removal test; and the 07-15 rewrite deleted the calibration, reflex-font lanes, color strategies, and commit-every-atom language that had held off the cream-editorial default since the alpha era. Five of six frozen craft directions converged on the same warm-paper family and both builders obeyed them. This lands the repair on top of the in-progress simplification: - new-work.md: the script assigns the build index again on both scopes; catalog challengers are fused (challenger supplies form and grammar, product supplies every fact, clarity wins conflicts) and weighed on the two proven axes only; attended runs present one fully committed direction with re-roll and an optional steer instead of a ranked lineup; the color-strategy picker, reflex-face list, saturated-look calibration, first-viewport thesis and memory test, commit-every-atom, scroll pacing, and prove-don't-claim return; the direction contract returns as five lean blocks audited by the separate-agent finish. - concept-seed.mjs: PROMOTED INDEX becomes ASSIGNED INDEX with build-assignment semantics; self re-roll only on named factual grounds. - craft-floor.md: hook-active sessions act on findings instead of re-auditing; the Refuse list is framed as category defaults the brief can earn; a closing commitment line keeps a ban list from being the last word before code. - codex.md / shape.md: contract references restored for flow coherence. Adopts the concurrent session's ceremony cuts, softened challenger instruction, seed SOURCE IDs and --candidate-count, detector-ownership fix, and the removal of the hook-side contract audit (the audit now belongs to the separate reviewer at finish). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a9540b5fe3
commit
36e3c05ca7
+4
-133
@@ -26,7 +26,6 @@
|
||||
* loadDetector() -> Promise<{ detectText, detectHtml }>
|
||||
* matchesAnyGlob(filePath, globs)
|
||||
* normalizeScanTargets(primaryTargets, projectCwd)
|
||||
* extractDirectionContract(content) / renderContractAudit(entries, opts)
|
||||
* runHook(deps) -> { exitCode, stdout, audit, reason? }
|
||||
* runStopHook(deps) -> { exitCode, stdout, audit, emission? }
|
||||
*
|
||||
@@ -1888,100 +1887,6 @@ export async function runHook({ stdinJson, env = {}, cwd = process.cwd(), now =
|
||||
}
|
||||
}
|
||||
|
||||
// ── Direction-contract audit ─────────────────────────────────────────────
|
||||
// The skill's decide-then-build step opens the built HTML artifact with a
|
||||
// DIRECTION CONTRACT comment (THESIS / OWN-WORLD / STORY / FIRST VIEWPORT /
|
||||
// BAR-RAISER / FORM blocks). At Stop time the deep pass extracts that
|
||||
// comment and feeds it back so the model audits the render against its own
|
||||
// promises. Proven in the eval harness: sample contracts promised radical
|
||||
// compositions and the build shipped the standard template anyway, because
|
||||
// nothing ever judged the build against the contract. Zero extra API calls:
|
||||
// this is a message, not a judge. It fires at most once per file per session
|
||||
// (a `contractAudited` flag on the session cache entry, the same state the
|
||||
// deep pass uses for finding dedupe) and rides the Stop pass's existing
|
||||
// emission rather than adding another block round.
|
||||
|
||||
// How far into the file to look for the leading comment. A contract lives at
|
||||
// the very top of the artifact; anything deeper is not the contract.
|
||||
export const CONTRACT_HEAD_CHARS = 6000;
|
||||
// The contract/concept marker must appear early in the comment body, so a
|
||||
// license header or unrelated note does not get mistaken for a contract.
|
||||
export const CONTRACT_MARKER_CHARS = 200;
|
||||
// Cap the extracted contract so a rambling comment cannot blow up the
|
||||
// Stop message.
|
||||
export const CONTRACT_MAX_CHARS = 1800;
|
||||
// Cap contract sections per Stop emission so many touched artifacts cannot
|
||||
// stack an unbounded message.
|
||||
export const CONTRACT_AUDIT_MAX_FILES = 3;
|
||||
export const CONTRACT_EXTS = new Set(['.html', '.htm', '.astro', '.svelte', '.vue', '.jsx', '.tsx']);
|
||||
export const CONTRACT_REQUIRED_FIELDS = ['THESIS', 'OWN-WORLD', 'STORY', 'FIRST VIEWPORT', 'BAR-RAISER', 'FORM'];
|
||||
|
||||
/**
|
||||
* Extract the artifact's own direction-contract comment from the head of an
|
||||
* HTML or component file. Supports HTML-family comments and JSX block
|
||||
* comments so the contract works in the Astro/Svelte/Vue/React scaffolds the
|
||||
* skill actually builds. Returns the trimmed, length-capped body, or null
|
||||
* when the file carries no valid contract block.
|
||||
*/
|
||||
export function extractDirectionContract(content) {
|
||||
if (typeof content !== 'string' || !content) return null;
|
||||
const head = content.slice(0, CONTRACT_HEAD_CHARS);
|
||||
const candidates = [];
|
||||
for (const pattern of [/<!--([\s\S]*?)-->/g, /\{\/\*([\s\S]*?)\*\/\}/g]) {
|
||||
for (const match of head.matchAll(pattern)) {
|
||||
const index = match.index ?? 0;
|
||||
const linePrefix = head.slice(head.lastIndexOf('\n', index - 1) + 1, index).trim();
|
||||
if (linePrefix.startsWith('//')) continue;
|
||||
candidates.push({ index, body: match[1].trim() });
|
||||
}
|
||||
}
|
||||
candidates.sort((a, b) => a.index - b.index);
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate.body || !/direction\s+contract|concept\s+contract/i.test(candidate.body.slice(0, CONTRACT_MARKER_CHARS))) continue;
|
||||
return candidate.body.slice(0, CONTRACT_MAX_CHARS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function missingDirectionContractFields(contract) {
|
||||
const body = typeof contract === 'string' ? contract : '';
|
||||
return CONTRACT_REQUIRED_FIELDS.filter((field) => {
|
||||
const label = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
|
||||
return !new RegExp(`\\b${label}\\s*:`, 'i').test(body);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the contract-audit section of the Stop message. `entries` is
|
||||
* [{ filePath, contract }]; at most CONTRACT_AUDIT_MAX_FILES are shown.
|
||||
* The two failure shapes named here are the ones observed in practice:
|
||||
* a promise the pixels do not deliver, and a contract whose own plan is
|
||||
* the standard template wearing the concept's nouns.
|
||||
*/
|
||||
export function renderContractAudit(entries, opts = {}) {
|
||||
if (!Array.isArray(entries) || entries.length === 0) return '';
|
||||
const cwd = opts.cwd || process.cwd();
|
||||
const shown = entries.slice(0, CONTRACT_AUDIT_MAX_FILES);
|
||||
const blocks = shown.map(({ filePath, contract }) => {
|
||||
const display = relativize(filePath, cwd);
|
||||
const missing = missingDirectionContractFields(contract);
|
||||
const integrity = missing.length > 0
|
||||
? `\n\nContract integrity defect: missing ${missing.join(', ')}. Repair the contract and the implementation together.`
|
||||
: '';
|
||||
return `${display} opens with this direction contract, written when the direction was decided:\n\n${contract}${integrity}`;
|
||||
});
|
||||
return [
|
||||
`${ENVELOPE_PREFIX} Direction-contract audit. Before finishing, audit the rendered page against the contract it opens with, promise by promise.`,
|
||||
'',
|
||||
blocks.join('\n\n'),
|
||||
'',
|
||||
'Two failure shapes to check honestly:',
|
||||
'1. A promise that is not in the pixels: the contract describes a composition or structure the built page does not deliver, because the build fell back to a standard arrangement, possibly one the contract explicitly rejects. Rebuild that part until the render matches the promise.',
|
||||
"2. A contract whose own section plan is the standard template wearing the concept's nouns: if a neighboring product could ship the same sequence of sections under different labels, revise the plan and the page together.",
|
||||
'State each promise and whether the render delivers it, and fix every gap before finishing.',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
// Cap on files the Stop deep pass will scan. The touched-file list is
|
||||
// session-scoped and already capped per edit, but a very long session could
|
||||
// accumulate more than the 30s hook timeout comfortably covers.
|
||||
@@ -1990,10 +1895,7 @@ export const STOP_MAX_FILES = 20;
|
||||
/**
|
||||
* Run the Stop-event deep pass: the FULL detector rule set over every UI
|
||||
* file touched this session, surfaced once, deduped against everything the
|
||||
* per-edit hook already reported. Touched HTML artifacts that open with a
|
||||
* direction-contract comment additionally get a one-time contract-audit
|
||||
* section appended after the detector findings (see extractDirectionContract
|
||||
* above). Same result contract as runHook():
|
||||
* per-edit hook already reported. Same result contract as runHook():
|
||||
* { exitCode, stdout, audit, emission? }
|
||||
*
|
||||
* Never throws; exits silent (and fast) when the session touched no UI
|
||||
@@ -2060,7 +1962,6 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no
|
||||
const scanOptions = designSystemOptions(config, det, projectCwd);
|
||||
|
||||
const freshGroups = [];
|
||||
const contractEntries = [];
|
||||
let scanned = 0;
|
||||
for (const filePath of touched) {
|
||||
if (scanned >= STOP_MAX_FILES) break;
|
||||
@@ -2082,22 +1983,6 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no
|
||||
? configuredExt.engine === 'html'
|
||||
: (ext === '.html' || ext === '.htm');
|
||||
|
||||
// Direction-contract audit: HTML and component artifacts, at most once per file
|
||||
// per session. The flag lives on the same session cache entry the
|
||||
// finding dedupe uses, so a second Stop fire stays quiet about it.
|
||||
const contractCapable = useHtmlEngine || CONTRACT_EXTS.has(ext);
|
||||
if (contractCapable) {
|
||||
const fileEntry = ensureFile(cache, sessionId, filePath);
|
||||
if (!fileEntry.contractAudited) {
|
||||
const contract = extractDirectionContract(content);
|
||||
if (contract) {
|
||||
fileEntry.contractAudited = true;
|
||||
ensureSession(cache, sessionId).updatedAt = Date.now();
|
||||
contractEntries.push({ filePath, contract });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useHtmlEngine && typeof det.detectHtml === 'function') {
|
||||
try { findings = await det.detectHtml(filePath, scanOptions); } catch { findings = []; }
|
||||
} else {
|
||||
@@ -2116,41 +2001,27 @@ export async function runStopHook({ stdinJson, env = {}, cwd = process.cwd(), no
|
||||
}
|
||||
audit.scannedFiles = scanned;
|
||||
|
||||
if (freshGroups.length === 0 && contractEntries.length === 0) {
|
||||
if (freshGroups.length === 0) {
|
||||
return result({ emitted: false, skipped: 'stop-clean', durationMs: Date.now() - started });
|
||||
}
|
||||
|
||||
// Fresh findings and first-time contract audits earn the cache write;
|
||||
// both mark this batch as surfaced so the next Stop fire is silent
|
||||
// Fresh findings earn the cache write so the next Stop fire is silent
|
||||
// unless new issues appear.
|
||||
persistCache(projectCwd, cache);
|
||||
|
||||
// Detector findings first, then the contract audit. Both ride the same
|
||||
// single Stop emission: the audit never adds an extra block round.
|
||||
const parts = [];
|
||||
if (freshGroups.length > 0) {
|
||||
parts.push(renderGroupedTemplate(freshGroups, config, { cwd: projectCwd }));
|
||||
}
|
||||
if (contractEntries.length > 0) {
|
||||
parts.push(renderContractAudit(contractEntries, { cwd: projectCwd }));
|
||||
}
|
||||
const text = appendDesignSystemNote(parts.join('\n\n'), scanOptions);
|
||||
const text = appendDesignSystemNote(renderGroupedTemplate(freshGroups, config, { cwd: projectCwd }), scanOptions);
|
||||
return {
|
||||
exitCode: 0,
|
||||
stdout: payload(text, 'Stop', harness),
|
||||
emission: {
|
||||
kind: 'stop-deep-pass',
|
||||
groups: freshGroups,
|
||||
...(contractEntries.length > 0
|
||||
? { contractFiles: contractEntries.map((entry) => entry.filePath) }
|
||||
: {}),
|
||||
},
|
||||
audit: {
|
||||
...audit,
|
||||
emitted: true,
|
||||
freshFiles: freshGroups.length,
|
||||
freshFindings: freshGroups.reduce((sum, group) => sum + group.findings.length, 0),
|
||||
...(contractEntries.length > 0 ? { contractAudits: contractEntries.length } : {}),
|
||||
chars: text.length,
|
||||
durationMs: Date.now() - started,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user