sneakoscope 0.7.13 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli/main.mjs +31 -5
- package/src/cli/maintenance-commands.mjs +5 -0
- package/src/core/fsx.mjs +1 -1
- package/src/core/perf-bench.mjs +9 -0
- package/src/core/pipeline.mjs +111 -3
- package/src/core/proof-field.mjs +129 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.16",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
package/src/cli/main.mjs
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
import { contextCapsule } from '../core/triwiki-attention.mjs';
|
|
40
40
|
import { rgbaKey, rgbaToWikiCoord, validateWikiCoordinateIndex } from '../core/wiki-coordinate.mjs';
|
|
41
41
|
import { ALLOWED_REASONING_EFFORTS, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_SOURCE_INVENTORY_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, FROM_CHAT_IMG_VISUAL_MAP_ARTIFACT, FROM_CHAT_IMG_WORK_ORDER_ARTIFACT, GETDESIGN_REFERENCE, RECOMMENDED_SKILLS, ROUTES, USAGE_TOPICS, context7ConfigToml, hasContext7ConfigText, hasFromChatImgSignal, looksLikeAnswerOnlyRequest, noUnrequestedFallbackCodePolicyText, reflectionRequiredForRoute, reasoningInstruction, routePrompt, routeReasoning, routeRequiresSubagents, speedLanePolicyText, stackCurrentDocsPolicy, triwikiContextTracking } from '../core/routes.mjs';
|
|
42
|
-
import { PIPELINE_PLAN_ARTIFACT, buildPipelinePlan, context7Evidence, evaluateStop, recordContext7Evidence, recordSubagentEvidence, validatePipelinePlan, writePipelinePlan } from '../core/pipeline.mjs';
|
|
42
|
+
import { PIPELINE_PLAN_ARTIFACT, buildPipelinePlan, context7Evidence, evaluateStop, projectGateStatus, recordContext7Evidence, recordSubagentEvidence, validatePipelinePlan, writePipelinePlan } from '../core/pipeline.mjs';
|
|
43
43
|
import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../core/team-dag.mjs';
|
|
44
44
|
import { appendTeamEvent, initTeamLive, parseTeamSpecText, readTeamDashboard, readTeamLive, readTeamTranscriptTail, renderTeamAgentLane } from '../core/team-live.mjs';
|
|
45
45
|
import { ARTIFACT_FILES, validateDogfoodReport, validateEffortDecision, validateFromChatImgVisualMap, validateSkillCandidate, validateSkillInjectionDecision, validateTeamDashboardState, validateWorkOrderLedger } from '../core/artifact-schemas.mjs';
|
|
@@ -469,11 +469,13 @@ async function pipeline(sub = 'status', args = []) {
|
|
|
469
469
|
const state = await readJson(stateFile(root), {});
|
|
470
470
|
const evidence = await context7Evidence(root, state);
|
|
471
471
|
const plan = state.mission_id ? await readJson(path.join(missionDir(root, state.mission_id), PIPELINE_PLAN_ARTIFACT), null) : null;
|
|
472
|
+
const gateProjection = await projectGateStatus(root, state);
|
|
472
473
|
const stop = await evaluateStop(root, state, { last_assistant_message: 'SKS Honest Mode verification evidence gap' }, { noQuestion: false });
|
|
473
474
|
const result = {
|
|
474
475
|
root,
|
|
475
476
|
state,
|
|
476
477
|
context7: evidence,
|
|
478
|
+
gate_projection: gateProjection,
|
|
477
479
|
plan: plan ? pipelinePlanSummary(plan, root, state.mission_id) : null,
|
|
478
480
|
stop_gate: state.stop_gate || null,
|
|
479
481
|
next_action: stop?.reason || 'No active blocking route gate detected.'
|
|
@@ -492,6 +494,7 @@ async function pipeline(sub = 'status', args = []) {
|
|
|
492
494
|
}
|
|
493
495
|
console.log(`Reasoning: ${state.reasoning_effort || 'medium'}${state.reasoning_profile ? ` (${state.reasoning_profile})` : ''}${state.reasoning_temporary ? ' temporary' : ''}`);
|
|
494
496
|
console.log(`Stop gate: ${state.stop_gate || 'none'}`);
|
|
497
|
+
console.log(`Gate projection: ${gateProjection.ok ? 'ok' : `blocked (${gateProjection.blockers.join(', ')})`}`);
|
|
495
498
|
console.log(`Context7: ${state.context7_required ? (evidence.ok ? 'ok' : 'required-missing') : 'optional'} (${evidence.count || 0} event(s))`);
|
|
496
499
|
console.log(`Next: ${result.next_action}`);
|
|
497
500
|
}
|
|
@@ -520,10 +523,11 @@ async function pipelinePlan(root, args = []) {
|
|
|
520
523
|
const changedRaw = readOption(args, '--changed', '');
|
|
521
524
|
const proofField = flag(args, '--proof-field') ? await buildProofField(root, { intent, changedFiles: changedRaw ? changedRaw.split(',') : undefined }) : null;
|
|
522
525
|
const contract = dir ? await readJson(path.join(dir, 'decision-contract.json'), {}) : {};
|
|
526
|
+
const contractSealed = contract?.status === 'sealed' || Boolean(contract?.sealed_at || contract?.sealed_hash);
|
|
523
527
|
const ambiguity = {
|
|
524
|
-
required: Boolean(routeContext.clarification_gate || state.ambiguity_gate_required),
|
|
525
|
-
passed: Boolean(state.ambiguity_gate_passed || state.clarification_passed),
|
|
526
|
-
status: state.clarification_required ? 'awaiting_answers' : (state.ambiguity_gate_passed ? 'contract_sealed' : undefined),
|
|
528
|
+
required: Boolean(routeContext.clarification_gate || state.ambiguity_gate_required || contractSealed),
|
|
529
|
+
passed: Boolean(state.ambiguity_gate_passed || state.clarification_passed || contractSealed),
|
|
530
|
+
status: state.clarification_required ? 'awaiting_answers' : ((state.ambiguity_gate_passed || contractSealed) ? 'contract_sealed' : undefined),
|
|
527
531
|
contract_hash: contract?.sealed_hash || null
|
|
528
532
|
};
|
|
529
533
|
const planInput = { missionId: id || null, route, task: intent, required: Boolean(routeContext.context7_required || state.context7_required), ambiguity, proofField };
|
|
@@ -1719,6 +1723,27 @@ async function selftest() {
|
|
|
1719
1723
|
if (trippedStop) throw new Error('selftest failed: compliance loop guard did not terminally trip');
|
|
1720
1724
|
const loopBlocker = await readJson(path.join(loopMission.dir, 'hard-blocker.json'), null);
|
|
1721
1725
|
if (loopBlocker?.reason !== 'compliance_loop_guard_tripped') throw new Error('selftest failed: compliance loop guard did not write hard blocker');
|
|
1726
|
+
const clarificationMission = await createMission(tmp, { mode: 'team', prompt: 'visible question gate selftest' });
|
|
1727
|
+
await writeTextAtomic(path.join(clarificationMission.dir, 'questions.md'), '# Questions\n\n1. GOAL_PRECISE: What should be changed?\n');
|
|
1728
|
+
await writeJsonAtomic(path.join(clarificationMission.dir, 'required-answers.schema.json'), { slots: [{ id: 'GOAL_PRECISE', question: 'What should be changed?' }] });
|
|
1729
|
+
const clarificationState = {
|
|
1730
|
+
mission_id: clarificationMission.id,
|
|
1731
|
+
mode: 'TEAM',
|
|
1732
|
+
route_command: '$Team',
|
|
1733
|
+
phase: 'TEAM_CLARIFICATION_AWAITING_ANSWERS',
|
|
1734
|
+
clarification_required: true,
|
|
1735
|
+
implementation_allowed: false,
|
|
1736
|
+
ambiguity_gate_required: true,
|
|
1737
|
+
ambiguity_gate_passed: false,
|
|
1738
|
+
stop_gate: 'clarification-gate'
|
|
1739
|
+
};
|
|
1740
|
+
for (let i = 0; i < 5; i++) {
|
|
1741
|
+
const stop = await evaluateStop(tmp, clarificationState, { last_assistant_message: 'continuing implementation without visible questions' });
|
|
1742
|
+
if (stop?.decision !== 'block' || !String(stop.reason || '').includes('waiting for mandatory ambiguity-removal answers')) throw new Error('selftest failed: clarification gate did not hard-pause without visible questions');
|
|
1743
|
+
}
|
|
1744
|
+
if (await exists(path.join(clarificationMission.dir, 'hard-blocker.json'))) throw new Error('selftest failed: clarification gate used compliance hard-blocker instead of waiting for answers');
|
|
1745
|
+
const visibleQuestionStop = await evaluateStop(tmp, clarificationState, { last_assistant_message: 'Required questions still pending:\n1. GOAL_PRECISE: What should be changed?\n\nUse sks pipeline answer latest answers.json.' });
|
|
1746
|
+
if (visibleQuestionStop?.continue !== true) throw new Error('selftest failed: visible clarification question block did not allow the question-only turn to stop');
|
|
1722
1747
|
await setCurrent(tmp, loopState);
|
|
1723
1748
|
const dfixPromptHook = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'hook', 'user-prompt-submit'], {
|
|
1724
1749
|
cwd: tmp,
|
|
@@ -2852,13 +2877,14 @@ async function selftest() {
|
|
|
2852
2877
|
if (!harnessReport.forgetting.fixture.passed || !harnessReport.tmux.views.includes('Harness Experiments View') || !harnessReport.reliability.tool_error_taxonomy.includes('Unknown')) throw new Error('selftest failed: harness growth fixture incomplete');
|
|
2853
2878
|
const proofField = await proofFieldFixture();
|
|
2854
2879
|
if (!proofField.validation.ok || !validateProofFieldReport(proofField.report).ok) throw new Error('selftest failed: proof field report invalid');
|
|
2855
|
-
if (!proofField.checks.route_cone_selected || !proofField.checks.cli_cone_selected || !proofField.checks.catastrophic_guard_present || !proofField.checks.negative_release_work_recorded || !proofField.checks.outcome_rubric_present || !proofField.checks.adversarial_lenses_present || !proofField.checks.simplicity_score_usable || !proofField.checks.execution_fast_lane_selected) throw new Error('selftest failed: proof field fixture checks incomplete');
|
|
2880
|
+
if (!proofField.checks.route_cone_selected || !proofField.checks.cli_cone_selected || !proofField.checks.catastrophic_guard_present || !proofField.checks.negative_release_work_recorded || !proofField.checks.outcome_rubric_present || !proofField.checks.adversarial_lenses_present || !proofField.checks.route_economy_present || !proofField.checks.simplicity_score_usable || !proofField.checks.execution_fast_lane_selected) throw new Error('selftest failed: proof field fixture checks incomplete');
|
|
2856
2881
|
if (!speedLanePolicyText().includes('proof_field_fast_lane') || !proofField.report.execution_lane?.skip_when_fast?.includes('planning_debate')) throw new Error('selftest failed: Proof Field speed lane policy missing');
|
|
2857
2882
|
const fastPipelinePlan = buildPipelinePlan({ route: routePrompt('$Team small CLI help update'), task: 'small CLI help surface update', proofField: proofField.report });
|
|
2858
2883
|
if (!validatePipelinePlan(fastPipelinePlan).ok || fastPipelinePlan.runtime_lane?.lane !== 'proof_field_fast_lane' || !fastPipelinePlan.skipped_stages.includes('planning_debate') || !fastPipelinePlan.invariants.includes('no_unrequested_fallback_code')) throw new Error('selftest failed: pipeline plan did not encode fast lane stage skips and fallback guard');
|
|
2859
2884
|
const broadProofField = await buildProofField(tmp, { intent: 'database security route refactor', changedFiles: ['src/core/db-safety.mjs', 'src/core/routes.mjs', 'src/cli/main.mjs', 'README.md'] });
|
|
2860
2885
|
const broadPipelinePlan = buildPipelinePlan({ route: routePrompt('$Team database security route refactor'), task: 'database security route refactor', proofField: broadProofField });
|
|
2861
2886
|
if (!validatePipelinePlan(broadPipelinePlan).ok || broadPipelinePlan.runtime_lane?.lane === 'proof_field_fast_lane' || broadPipelinePlan.skipped_stages.includes('planning_debate')) throw new Error('selftest failed: pipeline plan did not fail closed for broad/security work');
|
|
2887
|
+
if (broadPipelinePlan.route_economy?.mode !== 'report_only' || !broadPipelinePlan.route_economy.active_team_triggers?.includes('broad_change_set') || !broadPipelinePlan.route_economy.verification_stage_cache_key) throw new Error('selftest failed: route economy projection missing from pipeline plan');
|
|
2862
2888
|
const workflowPerf = await runWorkflowPerfBench(tmp, {
|
|
2863
2889
|
iterations: 2,
|
|
2864
2890
|
intent: 'small CLI help surface update',
|
|
@@ -529,6 +529,8 @@ export async function perfCommand(sub, args = []) {
|
|
|
529
529
|
console.log(`Mode: ${report.metrics.decision_mode}`);
|
|
530
530
|
console.log(`Fast lane: ${report.metrics.fast_lane_eligible ? 'yes' : 'no'}`);
|
|
531
531
|
console.log(`Proof Field p95: ${report.metrics.proof_field_build_ms_p95}ms`);
|
|
532
|
+
console.log(`Contract clarity: ${report.metrics.contract_clarity_score}`);
|
|
533
|
+
console.log(`Workflow complexity: ${report.metrics.workflow_complexity_band} (${report.metrics.workflow_complexity_score})`);
|
|
532
534
|
console.log(`Proof cones: ${report.metrics.proof_cone_count}`);
|
|
533
535
|
console.log(`Negative work skipped: ${report.metrics.negative_work_skipped_count}`);
|
|
534
536
|
console.log(`Next: ${report.recommendation.next.join('; ')}`);
|
|
@@ -569,6 +571,9 @@ export async function proofFieldCommand(sub, args = []) {
|
|
|
569
571
|
console.log(`Mode: ${report.fast_lane_decision.mode}`);
|
|
570
572
|
console.log(`Eligible: ${report.fast_lane_decision.eligible ? 'yes' : 'no'}`);
|
|
571
573
|
if (report.fast_lane_decision.blockers.length) console.log(`Blockers: ${report.fast_lane_decision.blockers.join(', ')}`);
|
|
574
|
+
console.log(`Contract clarity: ${report.contract_clarity.score}${report.contract_clarity.ask_recommended ? ' (ask recommended)' : ''}`);
|
|
575
|
+
console.log(`Workflow complexity: ${report.workflow_complexity.band} (${report.workflow_complexity.score})`);
|
|
576
|
+
if (report.team_trigger_matrix.active_triggers.length) console.log(`Team triggers: ${report.team_trigger_matrix.active_triggers.join(', ')}`);
|
|
572
577
|
console.log(`Proof cones: ${report.proof_cones.map((cone) => cone.id).join(', ')}`);
|
|
573
578
|
console.log(`Verification: ${report.fast_lane_decision.verification.join('; ')}`);
|
|
574
579
|
console.log(`Report: ${path.relative(root, report.report_path)}`);
|
package/src/core/fsx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.7.
|
|
8
|
+
export const PACKAGE_VERSION = '0.7.16';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
package/src/core/perf-bench.mjs
CHANGED
|
@@ -105,6 +105,11 @@ export async function runWorkflowPerfBench(root, opts = {}) {
|
|
|
105
105
|
verification_count: verification.length,
|
|
106
106
|
negative_work_skipped_count: estimatedSavedWork,
|
|
107
107
|
simplicity_score: Number(proofField?.simplicity_scorecard?.score || 0),
|
|
108
|
+
contract_clarity_score: Number(proofField?.contract_clarity?.score || 0),
|
|
109
|
+
workflow_complexity_score: Number(proofField?.workflow_complexity?.score || 0),
|
|
110
|
+
workflow_complexity_band: proofField?.workflow_complexity?.band || null,
|
|
111
|
+
team_trigger_count: proofField?.team_trigger_matrix?.active_triggers?.length || 0,
|
|
112
|
+
verification_stage_cache_key: proofField?.verification_stage_cache?.cache_key || null,
|
|
108
113
|
outcome_criteria_passed: (proofField?.simplicity_scorecard?.criteria || []).filter((item) => item.passed).length,
|
|
109
114
|
proof_field_valid: proofValidation.ok,
|
|
110
115
|
pipeline_plan_valid: planValidation.ok
|
|
@@ -129,6 +134,10 @@ export function validateWorkflowPerfReport(report = {}) {
|
|
|
129
134
|
if (!report.metrics?.execution_lane) issues.push('execution_lane');
|
|
130
135
|
if (!report.metrics?.pipeline_lane) issues.push('pipeline_lane');
|
|
131
136
|
if (!Number.isFinite(Number(report.metrics?.simplicity_score))) issues.push('simplicity_score');
|
|
137
|
+
if (!Number.isFinite(Number(report.metrics?.contract_clarity_score))) issues.push('contract_clarity_score');
|
|
138
|
+
if (!Number.isFinite(Number(report.metrics?.workflow_complexity_score))) issues.push('workflow_complexity_score');
|
|
139
|
+
if (!report.metrics?.workflow_complexity_band) issues.push('workflow_complexity_band');
|
|
140
|
+
if (!report.metrics?.verification_stage_cache_key) issues.push('verification_stage_cache_key');
|
|
132
141
|
if (!report.proof_field || !validateProofFieldReport(report.proof_field).ok) issues.push('proof_field');
|
|
133
142
|
if (!report.pipeline_plan || !validatePipelinePlan(report.pipeline_plan).ok) issues.push('pipeline_plan');
|
|
134
143
|
if (!report.recommendation?.mode) issues.push('recommendation');
|
package/src/core/pipeline.mjs
CHANGED
|
@@ -65,6 +65,7 @@ export function buildPipelinePlan(input = {}) {
|
|
|
65
65
|
const verification = planVerification(route, proof);
|
|
66
66
|
const skipped = stages.filter((stage) => stage.status === 'skipped').map((stage) => stage.id);
|
|
67
67
|
const kept = stages.filter((stage) => stage.status !== 'skipped' && stage.status !== 'not_applicable').map((stage) => stage.id);
|
|
68
|
+
const routeEconomy = routeEconomyPlan(proof);
|
|
68
69
|
return {
|
|
69
70
|
schema_version: PIPELINE_PLAN_SCHEMA_VERSION,
|
|
70
71
|
generated_at: nowIso(),
|
|
@@ -94,6 +95,7 @@ export function buildPipelinePlan(input = {}) {
|
|
|
94
95
|
verification,
|
|
95
96
|
invariants: ['no_unrequested_fallback_code', 'listed_verification', 'triwiki_validate_before_final', 'honest_mode'],
|
|
96
97
|
proof_field: proof,
|
|
98
|
+
route_economy: routeEconomy,
|
|
97
99
|
skill_dream: input.skillDream || { attached: false, reason: 'skill dreaming uses cheap counters and only runs inventory at threshold' },
|
|
98
100
|
next_actions: planNextActions(route, task, ambiguity, lane),
|
|
99
101
|
no_unrequested_fallback_code: true
|
|
@@ -113,6 +115,7 @@ export function validatePipelinePlan(plan = {}) {
|
|
|
113
115
|
if (!plan.runtime_lane?.lane) issues.push('runtime_lane');
|
|
114
116
|
if (!Array.isArray(plan.stages) || !plan.stages.length) issues.push('stages');
|
|
115
117
|
if (!Array.isArray(plan.verification) || !plan.verification.length) issues.push('verification');
|
|
118
|
+
if (!plan.route_economy?.mode) issues.push('route_economy');
|
|
116
119
|
if (plan.no_unrequested_fallback_code !== true || !plan.invariants?.includes('no_unrequested_fallback_code')) issues.push('fallback_guard');
|
|
117
120
|
if (!plan.next_actions?.length) issues.push('next_actions');
|
|
118
121
|
return { ok: issues.length === 0, issues };
|
|
@@ -147,7 +150,37 @@ function normalizeProofField(report) {
|
|
|
147
150
|
keep: report.execution_lane?.keep || SPEED_LANE_POLICY.always_keep,
|
|
148
151
|
verification: report.execution_lane?.verification || report.fast_lane_decision?.verification || [],
|
|
149
152
|
proof_cones: (report.proof_cones || []).map((cone) => cone.id),
|
|
150
|
-
source_hash: report.source_hash || null
|
|
153
|
+
source_hash: report.source_hash || null,
|
|
154
|
+
contract_clarity: report.contract_clarity || null,
|
|
155
|
+
workflow_complexity: report.workflow_complexity || null,
|
|
156
|
+
team_trigger_matrix: report.team_trigger_matrix || null,
|
|
157
|
+
verification_stage_cache: report.verification_stage_cache || null
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function routeEconomyPlan(proof = {}) {
|
|
162
|
+
if (!proof.attached) {
|
|
163
|
+
return {
|
|
164
|
+
schema_version: 1,
|
|
165
|
+
mode: 'unavailable',
|
|
166
|
+
report_only: true,
|
|
167
|
+
reason: proof.reason || 'Proof Field not attached yet'
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const triggers = proof.team_trigger_matrix?.active_triggers || [];
|
|
171
|
+
return {
|
|
172
|
+
schema_version: 1,
|
|
173
|
+
mode: 'report_only',
|
|
174
|
+
report_only: true,
|
|
175
|
+
contract_clarity_score: Number(proof.contract_clarity?.score || 0),
|
|
176
|
+
contract_clarity_passed: proof.contract_clarity?.passed === true,
|
|
177
|
+
ask_recommended: proof.contract_clarity?.ask_recommended === true,
|
|
178
|
+
workflow_complexity_score: Number(proof.workflow_complexity?.score || 0),
|
|
179
|
+
workflow_complexity_band: proof.workflow_complexity?.band || null,
|
|
180
|
+
team_trigger_count: triggers.length,
|
|
181
|
+
active_team_triggers: triggers,
|
|
182
|
+
verification_stage_cache_key: proof.verification_stage_cache?.cache_key || null,
|
|
183
|
+
deletion_policy: 'do_not_delete_or_skip_pipeline_stages_until_report_only_metrics_are_calibrated'
|
|
151
184
|
};
|
|
152
185
|
}
|
|
153
186
|
|
|
@@ -920,11 +953,76 @@ function reflectionStopReason(state = {}, status = {}) {
|
|
|
920
953
|
return `SKS ${route} must run reflection before final. Write .sneakoscope/missions/${id}/${REFLECTION_ARTIFACT}, record real lessons in ${REFLECTION_MEMORY_PATH} when present, refresh/pack and validate TriWiki, then pass .sneakoscope/missions/${id}/${REFLECTION_GATE}.${missing}`;
|
|
921
954
|
}
|
|
922
955
|
|
|
956
|
+
export async function projectGateStatus(root, state = {}) {
|
|
957
|
+
const gates = [];
|
|
958
|
+
const id = state?.mission_id || null;
|
|
959
|
+
if (clarificationGatePending(state)) {
|
|
960
|
+
gates.push({
|
|
961
|
+
id: 'clarification-gate',
|
|
962
|
+
ok: false,
|
|
963
|
+
missing: ['explicit_user_answers', 'pipeline_answer'],
|
|
964
|
+
source: id ? `.sneakoscope/missions/${id}/questions.md` : null
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (state?.context7_required) {
|
|
968
|
+
const evidence = await context7Evidence(root, state);
|
|
969
|
+
gates.push({
|
|
970
|
+
id: 'context7-evidence',
|
|
971
|
+
ok: evidence.ok,
|
|
972
|
+
missing: evidence.ok ? [] : ['resolve-library-id', 'query-docs'],
|
|
973
|
+
source: id ? `.sneakoscope/missions/${id}/context7-evidence.jsonl` : '.sneakoscope/state/context7-evidence.jsonl'
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (state?.subagents_required) {
|
|
977
|
+
const evidence = await subagentEvidence(root, state);
|
|
978
|
+
gates.push({
|
|
979
|
+
id: 'subagent-evidence',
|
|
980
|
+
ok: evidence.ok,
|
|
981
|
+
missing: evidence.ok ? [] : ['spawn_agent_or_exception_evidence'],
|
|
982
|
+
source: id ? `.sneakoscope/missions/${id}/subagent-evidence.jsonl` : '.sneakoscope/state/subagent-evidence.jsonl'
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
if (id && state?.stop_gate && !['none', 'honest_mode'].includes(state.stop_gate)) {
|
|
986
|
+
const active = await passedActiveGate(root, state);
|
|
987
|
+
gates.push({
|
|
988
|
+
id: active.file || state.stop_gate,
|
|
989
|
+
ok: active.ok,
|
|
990
|
+
missing: active.missing || (active.ok ? [] : ['passed']),
|
|
991
|
+
source: active.file ? `.sneakoscope/missions/${id}/${active.file}` : null
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
const reflection = await reflectionGateStatus(root, state);
|
|
995
|
+
if (reflectionRequiredForState(state)) {
|
|
996
|
+
gates.push({
|
|
997
|
+
id: REFLECTION_GATE,
|
|
998
|
+
ok: reflection.ok,
|
|
999
|
+
missing: reflection.missing || [],
|
|
1000
|
+
source: id ? `.sneakoscope/missions/${id}/${REFLECTION_GATE}` : null
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
const blockers = gates.filter((gate) => !gate.ok).flatMap((gate) => gate.missing.map((item) => `${gate.id}:${item}`));
|
|
1004
|
+
return {
|
|
1005
|
+
schema_version: 1,
|
|
1006
|
+
generated_at: nowIso(),
|
|
1007
|
+
mission_id: id,
|
|
1008
|
+
mode: state?.mode || null,
|
|
1009
|
+
report_only: true,
|
|
1010
|
+
ok: blockers.length === 0,
|
|
1011
|
+
blockers,
|
|
1012
|
+
gates
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
|
|
923
1016
|
export async function evaluateStop(root, state, payload, opts = {}) {
|
|
924
1017
|
const last = extractLastMessage(payload);
|
|
925
|
-
if (
|
|
1018
|
+
if (clarificationGatePending(state)) {
|
|
926
1019
|
if (await hasVisibleClarificationQuestionBlock(root, state, last)) return { continue: true };
|
|
927
|
-
return
|
|
1020
|
+
return {
|
|
1021
|
+
decision: 'block',
|
|
1022
|
+
reason: await clarificationStopReason(root, state, 'route'),
|
|
1023
|
+
gate: 'clarification',
|
|
1024
|
+
missing: ['explicit_user_answers', 'pipeline_answer']
|
|
1025
|
+
};
|
|
928
1026
|
}
|
|
929
1027
|
if (state?.context7_required && !(await hasContext7DocsEvidence(root, state))) {
|
|
930
1028
|
return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} requires Context7 evidence before completion. Use Context7 resolve-library-id, then query-docs (or legacy get-library-docs), so SKS can record context7-evidence.jsonl.`, { gate: 'context7-evidence' });
|
|
@@ -955,6 +1053,16 @@ export async function evaluateStop(root, state, payload, opts = {}) {
|
|
|
955
1053
|
return null;
|
|
956
1054
|
}
|
|
957
1055
|
|
|
1056
|
+
function clarificationGatePending(state = {}) {
|
|
1057
|
+
return Boolean(state?.clarification_required && String(state.phase || '').includes('CLARIFICATION_AWAITING_ANSWERS'))
|
|
1058
|
+
|| Boolean(
|
|
1059
|
+
state?.mission_id
|
|
1060
|
+
&& state.implementation_allowed === false
|
|
1061
|
+
&& state.ambiguity_gate_required === true
|
|
1062
|
+
&& state.ambiguity_gate_passed !== true
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
958
1066
|
async function complianceBlock(root, state = {}, reason = '', detail = {}) {
|
|
959
1067
|
if (!state?.mission_id) return { decision: 'block', reason };
|
|
960
1068
|
const dir = missionDir(root, state.mission_id);
|
package/src/core/proof-field.mjs
CHANGED
|
@@ -82,8 +82,12 @@ export async function buildProofField(root, opts = {}) {
|
|
|
82
82
|
const negativeWork = buildNegativeWorkCache(selectedCones, risk);
|
|
83
83
|
const fastLane = fastLaneDecision({ changedFiles, selectedCones, risk, negativeWork });
|
|
84
84
|
const sourceHash = await sourceDigest(root, changedFiles);
|
|
85
|
-
const
|
|
86
|
-
const
|
|
85
|
+
const contractClarity = contractClarityScore({ intent, changedFiles, selectedCones, risk });
|
|
86
|
+
const workflowComplexity = workflowComplexityScore({ changedFiles, selectedCones, risk, verification: fastLane.verification });
|
|
87
|
+
const teamTriggerMatrix = teamTriggerDecision({ intent, changedFiles, risk });
|
|
88
|
+
const verificationStageCache = verificationStageCachePlan({ sourceHash, changedFiles, verification: fastLane.verification });
|
|
89
|
+
const simplicity = outcomeScorecard({ intent, changedFiles, selectedCones, risk, negativeWork, fastLane, workflowComplexity });
|
|
90
|
+
const executionLane = executionLaneDecision({ fastLane, simplicity, workflowComplexity, teamTriggerMatrix });
|
|
87
91
|
return {
|
|
88
92
|
schema_version: PROOF_FIELD_SCHEMA_VERSION,
|
|
89
93
|
generated_at: nowIso(),
|
|
@@ -94,6 +98,10 @@ export async function buildProofField(root, opts = {}) {
|
|
|
94
98
|
invariant_ledger: INVARIANT_LEDGER,
|
|
95
99
|
outcome_rubric: OUTCOME_RUBRIC,
|
|
96
100
|
speed_lane_policy: SPEED_LANE_POLICY,
|
|
101
|
+
contract_clarity: contractClarity,
|
|
102
|
+
workflow_complexity: workflowComplexity,
|
|
103
|
+
team_trigger_matrix: teamTriggerMatrix,
|
|
104
|
+
verification_stage_cache: verificationStageCache,
|
|
97
105
|
simplicity_scorecard: simplicity,
|
|
98
106
|
execution_lane: executionLane,
|
|
99
107
|
proof_cones: selectedCones,
|
|
@@ -120,6 +128,11 @@ export function validateProofFieldReport(report = {}) {
|
|
|
120
128
|
if (!Array.isArray(report.simplicity_scorecard?.criteria) || report.simplicity_scorecard.criteria.length !== OUTCOME_RUBRIC.length) issues.push('simplicity_criteria');
|
|
121
129
|
if (!report.simplicity_scorecard?.criteria?.every((item) => item.adversarial_lens)) issues.push('simplicity_adversarial_lenses');
|
|
122
130
|
if (!report.speed_lane_policy || Number(report.speed_lane_policy.min_score) !== FAST_LANE_MIN_SCORE) issues.push('speed_lane_policy');
|
|
131
|
+
if (!Number.isFinite(Number(report.contract_clarity?.score))) issues.push('contract_clarity');
|
|
132
|
+
if (!Array.isArray(report.contract_clarity?.components) || report.contract_clarity.components.length < 1) issues.push('contract_clarity_components');
|
|
133
|
+
if (!Number.isFinite(Number(report.workflow_complexity?.score))) issues.push('workflow_complexity');
|
|
134
|
+
if (!Array.isArray(report.team_trigger_matrix?.triggers)) issues.push('team_trigger_matrix');
|
|
135
|
+
if (report.verification_stage_cache?.report_only !== true || !report.verification_stage_cache?.cache_key) issues.push('verification_stage_cache');
|
|
123
136
|
if (!report.execution_lane?.lane) issues.push('execution_lane');
|
|
124
137
|
if (report.execution_lane?.lane === SPEED_LANE_POLICY.fast_lane && report.execution_lane?.score < FAST_LANE_MIN_SCORE) issues.push('execution_lane_score');
|
|
125
138
|
if (!Array.isArray(report.proof_cones)) issues.push('proof_cones');
|
|
@@ -144,6 +157,7 @@ export async function proofFieldFixture() {
|
|
|
144
157
|
negative_release_work_recorded: report.negative_work_cache.some((item) => item.id === 'full_release_gate' && item.disposition === 'skip_with_evidence'),
|
|
145
158
|
outcome_rubric_present: report.outcome_rubric.length === OUTCOME_RUBRIC.length,
|
|
146
159
|
adversarial_lenses_present: report.outcome_rubric.every((item) => item.adversarial_lens) && report.simplicity_scorecard.criteria.every((item) => item.adversarial_lens),
|
|
160
|
+
route_economy_present: report.contract_clarity?.report_only === true && report.workflow_complexity?.report_only === true && report.team_trigger_matrix?.report_only === true && report.verification_stage_cache?.report_only === true,
|
|
147
161
|
simplicity_score_usable: Number(report.simplicity_scorecard?.score) >= FAST_LANE_MIN_SCORE,
|
|
148
162
|
execution_fast_lane_selected: report.execution_lane?.lane === SPEED_LANE_POLICY.fast_lane
|
|
149
163
|
}
|
|
@@ -237,11 +251,120 @@ function fastLaneDecision({ changedFiles, selectedCones, risk, negativeWork }) {
|
|
|
237
251
|
};
|
|
238
252
|
}
|
|
239
253
|
|
|
240
|
-
function
|
|
254
|
+
function contractClarityScore({ intent, changedFiles, selectedCones, risk }) {
|
|
255
|
+
const components = [
|
|
256
|
+
{
|
|
257
|
+
id: 'goal_fit',
|
|
258
|
+
floor: 0.7,
|
|
259
|
+
score: intent ? 1 : 0.25,
|
|
260
|
+
evidence: intent ? 'intent provided' : 'missing explicit intent'
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: 'safety_scope_clarity',
|
|
264
|
+
floor: 0.7,
|
|
265
|
+
score: risk.flags.unknown_surface ? 0.4 : 1,
|
|
266
|
+
evidence: risk.flags.unknown_surface ? 'unknown surface present' : `risk flags classified: ${Object.entries(risk.flags).filter(([, value]) => value).map(([key]) => key).join(', ') || 'none'}`
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: 'acceptance_observability',
|
|
270
|
+
floor: 0.6,
|
|
271
|
+
score: /accept|verify|test|done|complete|검증|완료|구현|개선/i.test(intent || '') ? 1 : 0.65,
|
|
272
|
+
evidence: /accept|verify|test|done|complete|검증|완료|구현|개선/i.test(intent || '') ? 'observable outcome language present' : 'acceptance inferred from proof cones'
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'write_scope_certainty',
|
|
276
|
+
floor: 0.7,
|
|
277
|
+
score: changedFiles.length > 0 && changedFiles.length <= 3 ? 1 : (changedFiles.length > 0 ? 0.55 : 0.2),
|
|
278
|
+
evidence: `${changedFiles.length} changed file(s) in proposed scope`
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'context_confidence',
|
|
282
|
+
floor: 0.7,
|
|
283
|
+
score: selectedCones.length > 0 && !risk.flags.unknown_surface ? 1 : 0.45,
|
|
284
|
+
evidence: `${selectedCones.length} proof cone(s), unknown_surface=${risk.flags.unknown_surface}`
|
|
285
|
+
}
|
|
286
|
+
];
|
|
287
|
+
const score = Number((components.reduce((sum, item) => sum + item.score, 0) / components.length).toFixed(2));
|
|
288
|
+
const failed = components.filter((item) => item.score < item.floor).map((item) => item.id);
|
|
289
|
+
return {
|
|
290
|
+
schema_version: 1,
|
|
291
|
+
report_only: true,
|
|
292
|
+
score,
|
|
293
|
+
passed: score >= 0.8 && failed.length === 0,
|
|
294
|
+
ask_recommended: failed.length > 0,
|
|
295
|
+
failed_floors: failed,
|
|
296
|
+
components
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function workflowComplexityScore({ changedFiles, selectedCones, risk, verification }) {
|
|
301
|
+
const surfaces = new Set(selectedCones.flatMap((cone) => cone.surfaces || []));
|
|
302
|
+
const weighted = {
|
|
303
|
+
changed_files: Math.min(changedFiles.length, 8) / 8 * 0.3,
|
|
304
|
+
proof_cones: Math.min(selectedCones.length, 4) / 4 * 0.2,
|
|
305
|
+
risk_flags: Math.min(risk.score, 4) / 4 * 0.25,
|
|
306
|
+
verification: Math.min((verification || []).length, 6) / 6 * 0.15,
|
|
307
|
+
surfaces: Math.min(surfaces.size, 6) / 6 * 0.1
|
|
308
|
+
};
|
|
309
|
+
const score = Number(Object.values(weighted).reduce((sum, value) => sum + value, 0).toFixed(2));
|
|
310
|
+
const band = score >= 0.67 ? 'frontier' : (score >= 0.34 ? 'balanced' : 'focused');
|
|
311
|
+
return {
|
|
312
|
+
schema_version: 1,
|
|
313
|
+
report_only: true,
|
|
314
|
+
score,
|
|
315
|
+
band,
|
|
316
|
+
inputs: {
|
|
317
|
+
changed_file_count: changedFiles.length,
|
|
318
|
+
proof_cone_count: selectedCones.length,
|
|
319
|
+
route_surface_count: surfaces.size,
|
|
320
|
+
risk_flag_count: risk.score,
|
|
321
|
+
verification_count: (verification || []).length
|
|
322
|
+
},
|
|
323
|
+
weighted
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function teamTriggerDecision({ intent, changedFiles, risk }) {
|
|
328
|
+
const triggers = [
|
|
329
|
+
{ id: 'explicit_team', active: /\$?team\b/i.test(intent || ''), reason: 'user explicitly selected Team' },
|
|
330
|
+
{ id: 'broad_change_set', active: changedFiles.length > 3, reason: `${changedFiles.length} changed file(s)` },
|
|
331
|
+
{ id: 'database_surface', active: risk.flags.database, reason: 'database risk flag present' },
|
|
332
|
+
{ id: 'security_surface', active: risk.flags.security, reason: 'security risk flag present' },
|
|
333
|
+
{ id: 'visual_forensic_surface', active: risk.flags.visual_forensic, reason: 'visual forensic risk flag present' },
|
|
334
|
+
{ id: 'unknown_surface', active: risk.flags.unknown_surface, reason: 'unknown proof cone selected' },
|
|
335
|
+
{ id: 'unsupported_claim', active: false, reason: 'only activated by Honest/H-Proof evidence' },
|
|
336
|
+
{ id: 'verification_failed', active: false, reason: 'only activated after verification failure' }
|
|
337
|
+
];
|
|
338
|
+
const active = triggers.filter((trigger) => trigger.active).map((trigger) => trigger.id);
|
|
339
|
+
return {
|
|
340
|
+
schema_version: 1,
|
|
341
|
+
report_only: true,
|
|
342
|
+
full_team_recommended: active.length > 0,
|
|
343
|
+
active_triggers: active,
|
|
344
|
+
triggers
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function verificationStageCachePlan({ sourceHash, changedFiles, verification }) {
|
|
349
|
+
const stage1 = [...new Set(verification || [])].filter((command) => /packcheck|selftest|commands --json|wiki validate|eval run/i.test(command));
|
|
350
|
+
const cacheInput = { source_hash: sourceHash || null, changed_files: changedFiles, stage1 };
|
|
351
|
+
return {
|
|
352
|
+
schema_version: 1,
|
|
353
|
+
report_only: true,
|
|
354
|
+
status: sourceHash ? 'planned' : 'source_hash_missing',
|
|
355
|
+
cache_key: sha256(JSON.stringify(cacheInput)).slice(0, 16),
|
|
356
|
+
source_hash: sourceHash || null,
|
|
357
|
+
stage1_commands: stage1,
|
|
358
|
+
invalidates_on: ['source_hash_changed', 'command_changed', 'cwd_changed', 'env_changed'],
|
|
359
|
+
reuse_policy: 'fail_closed'
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function outcomeScorecard({ intent, changedFiles, selectedCones, risk, negativeWork, fastLane, workflowComplexity }) {
|
|
241
364
|
const skipped = negativeWork.filter((item) => item.disposition === 'skip_with_evidence').length;
|
|
242
365
|
const criteria = [
|
|
243
366
|
{ id: 'goal_fit', passed: Boolean(intent || changedFiles.length), evidence: intent ? 'intent provided' : 'changed files define scope' },
|
|
244
|
-
{ id: 'minimum_surface', passed: changedFiles.length <= 3 && !risk.flags.unknown_surface, evidence: `${changedFiles.length} changed file(s), ${selectedCones.length} proof cone(s)` },
|
|
367
|
+
{ id: 'minimum_surface', passed: changedFiles.length <= 3 && !risk.flags.unknown_surface, evidence: `${changedFiles.length} changed file(s), ${selectedCones.length} proof cone(s), complexity=${workflowComplexity?.band || 'unknown'}` },
|
|
245
368
|
{ id: 'bounded_verification', passed: fastLane.verification.length > 0 && fastLane.verification.length <= 4, evidence: `${fastLane.verification.length} focused verification command(s)` },
|
|
246
369
|
{ id: 'escalation_defined', passed: Array.isArray(fastLane.escalate_on) && fastLane.escalate_on.length > 0, evidence: `${fastLane.escalate_on.length} escalation trigger(s)` }
|
|
247
370
|
].map((criterion) => ({
|
|
@@ -258,7 +381,7 @@ function outcomeScorecard({ intent, changedFiles, selectedCones, risk, negativeW
|
|
|
258
381
|
};
|
|
259
382
|
}
|
|
260
383
|
|
|
261
|
-
function executionLaneDecision({ fastLane, simplicity }) {
|
|
384
|
+
function executionLaneDecision({ fastLane, simplicity, workflowComplexity, teamTriggerMatrix }) {
|
|
262
385
|
const score = Number(simplicity?.score || 0);
|
|
263
386
|
const fast = Boolean(fastLane?.eligible) && score >= FAST_LANE_MIN_SCORE;
|
|
264
387
|
const lane = fast
|
|
@@ -276,7 +399,7 @@ function executionLaneDecision({ fastLane, simplicity }) {
|
|
|
276
399
|
escalate_on: [...new Set([...(fastLane?.escalate_on || []), ...SPEED_LANE_POLICY.fail_closed_on])],
|
|
277
400
|
reason: fast
|
|
278
401
|
? `Proof Field score ${score} >= ${FAST_LANE_MIN_SCORE} with no fast-lane blockers`
|
|
279
|
-
: `Fast lane not allowed: mode=${fastLane?.mode || 'unknown'}, score=${score}, blockers=${(fastLane?.blockers || []).join(', ') || 'none'}`
|
|
402
|
+
: `Fast lane not allowed: mode=${fastLane?.mode || 'unknown'}, score=${score}, complexity=${workflowComplexity?.band || 'unknown'}, team_triggers=${(teamTriggerMatrix?.active_triggers || []).join(', ') || 'none'}, blockers=${(fastLane?.blockers || []).join(', ') || 'none'}`
|
|
280
403
|
};
|
|
281
404
|
}
|
|
282
405
|
|