sneakoscope 4.2.0 → 4.3.0

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.
Files changed (126) hide show
  1. package/README.md +35 -8
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/cli/command-registry.js +3 -1
  7. package/dist/cli/ultra-search-command.js +163 -0
  8. package/dist/cli/xai-command.js +28 -168
  9. package/dist/core/agents/agent-codex-cockpit.js +3 -3
  10. package/dist/core/agents/agent-runner-ollama.js +2 -0
  11. package/dist/core/agents/agent-wrongness.js +1 -1
  12. package/dist/core/agents/native-worker-backend-router.js +3 -0
  13. package/dist/core/bench.js +115 -0
  14. package/dist/core/code-structure.js +399 -11
  15. package/dist/core/codex-control/codex-app-server-v2-client.js +86 -2
  16. package/dist/core/codex-control/codex-fake-sdk-adapter.js +67 -9
  17. package/dist/core/codex-control/codex-reliability-shield.js +26 -5
  18. package/dist/core/codex-control/codex-task-runner.js +7 -1
  19. package/dist/core/codex-control/gpt-final-arbiter.js +4 -1
  20. package/dist/core/codex-control/gpt-final-review-schema.js +58 -0
  21. package/dist/core/codex-control/model-call-concurrency.js +1 -1
  22. package/dist/core/codex-native/core-skill-manifest.js +23 -0
  23. package/dist/core/commands/bench-command.js +11 -2
  24. package/dist/core/commands/code-structure-command.js +34 -2
  25. package/dist/core/commands/qa-loop-command.js +23 -7
  26. package/dist/core/commands/run-command.js +92 -2
  27. package/dist/core/commands/seo-command.js +130 -0
  28. package/dist/core/feature-fixtures.js +6 -0
  29. package/dist/core/feature-registry.js +3 -1
  30. package/dist/core/fsx.js +1 -1
  31. package/dist/core/hooks-runtime.js +9 -1
  32. package/dist/core/init.js +8 -6
  33. package/dist/core/lean-engineering-policy.js +159 -0
  34. package/dist/core/pipeline-internals/runtime-core.js +15 -5
  35. package/dist/core/proof/auto-finalize.js +3 -2
  36. package/dist/core/proof/proof-schema.js +2 -1
  37. package/dist/core/proof/proof-writer.js +1 -0
  38. package/dist/core/proof/route-adapter.js +4 -2
  39. package/dist/core/proof/route-finalizer.js +35 -3
  40. package/dist/core/qa-loop/qa-app-server-driver.js +134 -0
  41. package/dist/core/qa-loop/qa-contract-v2.js +231 -0
  42. package/dist/core/qa-loop/qa-gate-v2.js +132 -0
  43. package/dist/core/qa-loop/qa-runtime-artifacts.js +53 -0
  44. package/dist/core/qa-loop/qa-surface-router.js +114 -0
  45. package/dist/core/qa-loop/qa-types.js +18 -0
  46. package/dist/core/qa-loop.js +83 -26
  47. package/dist/core/release/gate-manifest.js +1 -0
  48. package/dist/core/release/sla-scheduler.js +1 -1
  49. package/dist/core/release-parallel-full-coverage.js +1 -1
  50. package/dist/core/routes.js +96 -14
  51. package/dist/core/search-visibility/adapter-registry.js +26 -0
  52. package/dist/core/search-visibility/adapters/next-app.js +6 -0
  53. package/dist/core/search-visibility/adapters/next-pages.js +6 -0
  54. package/dist/core/search-visibility/adapters/static-site.js +6 -0
  55. package/dist/core/search-visibility/analyzers.js +377 -0
  56. package/dist/core/search-visibility/artifacts.js +183 -0
  57. package/dist/core/search-visibility/discovery.js +347 -0
  58. package/dist/core/search-visibility/index.js +199 -0
  59. package/dist/core/search-visibility/mission.js +67 -0
  60. package/dist/core/search-visibility/mutation.js +314 -0
  61. package/dist/core/search-visibility/types.js +2 -0
  62. package/dist/core/search-visibility/verifier.js +60 -0
  63. package/dist/core/source-intelligence/source-intelligence-policy.js +45 -26
  64. package/dist/core/source-intelligence/source-intelligence-proof.js +10 -16
  65. package/dist/core/source-intelligence/source-intelligence-runner.js +56 -42
  66. package/dist/core/triwiki/triwiki-affected-graph.js +3 -2
  67. package/dist/core/trust-kernel/trust-report.js +3 -5
  68. package/dist/core/ultra-search/index.js +3 -0
  69. package/dist/core/ultra-search/runtime.js +502 -0
  70. package/dist/core/ultra-search/types.js +3 -0
  71. package/dist/core/version.js +1 -1
  72. package/dist/scripts/agent-visual-consistency-check.js +1 -1
  73. package/dist/scripts/check-architecture.js +40 -7
  74. package/dist/scripts/check-command-module-budget.js +43 -5
  75. package/dist/scripts/check-pipeline-budget.js +17 -30
  76. package/dist/scripts/check-publish-tag.js +33 -6
  77. package/dist/scripts/check-route-modularity.js +25 -33
  78. package/dist/scripts/check-runtime-schemas.js +22 -0
  79. package/dist/scripts/codex-control-all-pipelines-check.js +1 -0
  80. package/dist/scripts/codex-control-model-capacity-fallback-check.js +53 -0
  81. package/dist/scripts/config-managed-merge-callsite-coverage-check.js +7 -1
  82. package/dist/scripts/core-skill-immutable-sync-check.js +3 -2
  83. package/dist/scripts/core-skill-integrity-blackbox.js +3 -2
  84. package/dist/scripts/core-skill-manifest-check.js +7 -2
  85. package/dist/scripts/geo-claim-evidence-check.js +18 -0
  86. package/dist/scripts/geo-cli-blackbox-check.js +18 -0
  87. package/dist/scripts/geo-crawler-policy-check.js +16 -0
  88. package/dist/scripts/geo-llms-txt-optional-check.js +19 -0
  89. package/dist/scripts/gpt-final-arbiter-check.js +4 -1
  90. package/dist/scripts/loop-directive-check-lib.js +78 -1
  91. package/dist/scripts/qa-loop-app-server-driver-check.js +74 -0
  92. package/dist/scripts/qa-loop-surface-router-check.js +49 -0
  93. package/dist/scripts/release-check-dynamic-execute.js +1 -1
  94. package/dist/scripts/release-metadata-1-19-check.js +2 -2
  95. package/dist/scripts/release-parallel-check.js +17 -2
  96. package/dist/scripts/release-parallel-full-coverage-check.js +1 -1
  97. package/dist/scripts/release-readiness-report.js +6 -6
  98. package/dist/scripts/release-registry-check.js +33 -14
  99. package/dist/scripts/runtime-ts-rust-boundary-check.js +1 -1
  100. package/dist/scripts/search-visibility-gate-lib.js +124 -0
  101. package/dist/scripts/seo-audit-fixture-check.js +16 -0
  102. package/dist/scripts/seo-canonical-locale-check.js +19 -0
  103. package/dist/scripts/seo-cli-blackbox-check.js +18 -0
  104. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +18 -0
  105. package/dist/scripts/seo-geo-geo-disambiguation-check.js +12 -0
  106. package/dist/scripts/seo-geo-no-unsupported-ranking-claims-check.js +18 -0
  107. package/dist/scripts/seo-geo-route-identity-check.js +12 -0
  108. package/dist/scripts/seo-geo-skill-rich-content-check.js +22 -0
  109. package/dist/scripts/seo-mutation-rollback-check.js +23 -0
  110. package/dist/scripts/seo-no-mutation-by-default-check.js +17 -0
  111. package/dist/scripts/seo-structured-data-visible-content-check.js +19 -0
  112. package/dist/scripts/sks-1-18-gate-lib.js +2 -2
  113. package/dist/scripts/sks-3-1-5-directive-check-lib.js +10 -1
  114. package/dist/scripts/source-intelligence-all-modes-check.js +9 -19
  115. package/dist/scripts/source-intelligence-policy-check.js +6 -6
  116. package/dist/scripts/triwiki-affected-graph-check.js +2 -2
  117. package/dist/scripts/ultra-search-provider-interface-check.js +27 -0
  118. package/package.json +26 -5
  119. package/schemas/search-visibility/finding-ledger.schema.json +36 -0
  120. package/schemas/search-visibility/gate.schema.json +22 -0
  121. package/schemas/search-visibility/mutation-plan.schema.json +27 -0
  122. package/schemas/search-visibility/site-inventory.schema.json +21 -0
  123. package/schemas/search-visibility/verification-report.schema.json +23 -0
  124. package/dist/core/mcp/xai-mcp-detector.js +0 -157
  125. package/dist/core/mcp/xai-search-adapter.js +0 -100
  126. package/dist/scripts/xai-mcp-capability-check.js +0 -14
@@ -0,0 +1,231 @@
1
+ import { CODEX_APP_SERVER_DOC_URL } from '../routes.js';
2
+ import { nowIso } from '../fsx.js';
3
+ import { inferAuthMode, inferQaTargetKind, selectQaSurfaceForContract } from './qa-surface-router.js';
4
+ import { DEFAULT_QA_MAX_CYCLES, QA_LOOP_CONTRACT_VERSION } from './qa-types.js';
5
+ export function buildQaContractV2(legacy = {}, options = {}) {
6
+ const answers = legacy.answers || {};
7
+ const prompt = stringValue(legacy.prompt || answers.GOAL_PRECISE || '');
8
+ const targetUrl = normalizeUnset(answers.TARGET_BASE_URL) || firstUrl(prompt) || null;
9
+ const authMode = authModeFromAnswers(answers, prompt);
10
+ const kind = targetKindFromAnswers(answers, prompt, targetUrl, authMode);
11
+ const uiRequired = uiRequiredFromAnswers(answers);
12
+ const apiRequired = apiRequiredFromAnswers(answers);
13
+ const reportOnly = options.reportOnly === true || stringValue(answers.QA_CORRECTIVE_POLICY) === 'report_only_no_code_changes';
14
+ return {
15
+ schema: 'sks.qa-loop-contract.v2',
16
+ version: QA_LOOP_CONTRACT_VERSION,
17
+ generated_at: nowIso(),
18
+ mission_id: options.missionId || legacy.mission_id || null,
19
+ prompt,
20
+ target: {
21
+ url: targetUrl,
22
+ environment: normalizeUnset(answers.TARGET_ENVIRONMENT) || 'local_dev_server',
23
+ kind,
24
+ dev_server_command: normalizeUnset(answers.DEV_SERVER_COMMAND),
25
+ api_base_url: normalizeUnset(answers.API_BASE_URL)
26
+ },
27
+ scope: {
28
+ ui_required: uiRequired,
29
+ api_required: apiRequired,
30
+ visual_required: uiRequired,
31
+ gpt_image_2_review_required: gptImage2RequiredFromAnswers(answers, prompt)
32
+ },
33
+ auth: {
34
+ required: stringValue(answers.LOGIN_REQUIRED) === 'yes' || authMode !== 'not_required',
35
+ mode: authMode,
36
+ credential_storage: 'never_store_credentials_in_artifacts_or_wiki'
37
+ },
38
+ mutation: {
39
+ product_data_policy: productDataPolicyFromAnswers(answers),
40
+ source_code_patch_policy: reportOnly ? 'disabled_report_only' : 'enabled',
41
+ production_side_effects: 'blocked_by_default',
42
+ destructive_deployed_tests: 'never'
43
+ },
44
+ runtime: {
45
+ max_cycles: boundedInteger(options.maxCycles ?? answers.MAX_QA_CYCLES, 1, 50, DEFAULT_QA_MAX_CYCLES),
46
+ no_progress_stop: true,
47
+ convergence_stop: true,
48
+ same_flow_replay_required: true,
49
+ no_headless_only_pass: true,
50
+ no_artifact_only_pass: true,
51
+ no_mock_as_real: true
52
+ },
53
+ cli: {
54
+ report_only: reportOnly,
55
+ requested_surface: options.requestedSurface || surfaceOverrideFromAnswers(answers),
56
+ fix_mode: reportOnly ? 'report_only' : 'safe_local_code_fixes'
57
+ },
58
+ legacy_contract_hash: legacy.sealed_hash || null
59
+ };
60
+ }
61
+ export function buildQaJourneyGraphV2(contract) {
62
+ const surface = selectQaSurfaceForContract(contract).selected_surface;
63
+ const steps = journeyStepsForContract(contract, surface);
64
+ return {
65
+ schema: 'sks.qa-loop-journey-graph.v2',
66
+ generated_at: nowIso(),
67
+ mission_id: contract.mission_id,
68
+ surface,
69
+ target: contract.target.url,
70
+ replay_fingerprint: replayFingerprint(contract, steps),
71
+ steps,
72
+ same_flow_replay_required: contract.runtime.same_flow_replay_required
73
+ };
74
+ }
75
+ export function buildQaAuthDataSandboxPolicy(contract) {
76
+ return {
77
+ schema: 'sks.qa-loop-auth-data-sandbox-policy.v2',
78
+ generated_at: nowIso(),
79
+ mission_id: contract.mission_id,
80
+ auth: {
81
+ required: contract.auth.required,
82
+ mode: contract.auth.mode,
83
+ credential_storage: contract.auth.credential_storage,
84
+ artifact_redaction: 'credentials_cookies_tokens_sensitive_screenshots_redacted_or_omitted'
85
+ },
86
+ mutation: contract.mutation,
87
+ approvals: {
88
+ source_code_patch: contract.mutation.source_code_patch_policy,
89
+ product_data_mutation: contract.mutation.product_data_policy,
90
+ production_side_effects: contract.mutation.production_side_effects,
91
+ destructive_deployed_tests: contract.mutation.destructive_deployed_tests
92
+ },
93
+ docs_url: CODEX_APP_SERVER_DOC_URL
94
+ };
95
+ }
96
+ function journeyStepsForContract(contract, surface) {
97
+ const steps = [];
98
+ if (contract.scope.ui_required) {
99
+ steps.push({
100
+ id: 'open-target',
101
+ kind: 'open',
102
+ label: `Open target with ${surface}`,
103
+ target: contract.target.url,
104
+ expected: 'target renders without fatal error',
105
+ data_classification: contract.target.kind === 'local_web' ? 'local_fixture' : 'public'
106
+ });
107
+ steps.push({
108
+ id: 'exercise-primary-flow',
109
+ kind: 'click',
110
+ label: 'Exercise the primary user journey with real UI actions',
111
+ target: contract.target.url,
112
+ expected: 'actual state matches the expected journey state',
113
+ data_classification: contract.auth.required ? 'sensitive' : 'public'
114
+ });
115
+ steps.push({
116
+ id: 'assert-visible-state',
117
+ kind: 'assert',
118
+ label: 'Compare expected and observed rendered state',
119
+ target: contract.target.url,
120
+ expected: 'assertions are backed by action and observation ledger entries',
121
+ data_classification: 'public'
122
+ });
123
+ }
124
+ if (contract.scope.api_required) {
125
+ steps.push({
126
+ id: 'api-readiness',
127
+ kind: 'inspect',
128
+ label: 'Inspect safe API readiness and error states',
129
+ target: contract.target.api_base_url || contract.target.url,
130
+ expected: 'API checks stay inside the sandbox policy',
131
+ data_classification: 'public'
132
+ });
133
+ }
134
+ steps.push({
135
+ id: 'fix-and-replay',
136
+ kind: 'replay',
137
+ label: 'Replay exactly the same journey after each applied fix',
138
+ target: contract.target.url,
139
+ expected: 'every applied fix is followed by same-flow replay evidence',
140
+ data_classification: 'source_code'
141
+ });
142
+ return steps;
143
+ }
144
+ function targetKindFromAnswers(answers, prompt, targetUrl, authMode) {
145
+ const explicit = stringValue(answers.QA_TARGET_KIND);
146
+ if (isQaTargetKind(explicit))
147
+ return explicit;
148
+ return inferQaTargetKind({ prompt, targetUrl, authMode });
149
+ }
150
+ function authModeFromAnswers(answers, prompt) {
151
+ const explicit = stringValue(answers.QA_AUTH_MODE);
152
+ if (isQaAuthMode(explicit))
153
+ return explicit;
154
+ if (stringValue(answers.LOGIN_REQUIRED) === 'yes') {
155
+ if (stringValue(answers.TEMP_TEST_CREDENTIALS_READY) === 'yes_temp_only')
156
+ return 'runtime_ephemeral_credentials';
157
+ return 'blocked_missing_credentials';
158
+ }
159
+ return inferAuthMode(prompt);
160
+ }
161
+ function productDataPolicyFromAnswers(answers) {
162
+ const policy = stringValue(answers.QA_MUTATION_POLICY);
163
+ if (policy === 'seeded_create_change_remove_local_only')
164
+ return 'seeded_local_only';
165
+ if (policy === 'seeded_create_change_only')
166
+ return 'sandbox_only';
167
+ return 'read_only';
168
+ }
169
+ function surfaceOverrideFromAnswers(answers) {
170
+ const value = stringValue(answers.QA_SURFACE || answers.QA_REQUESTED_SURFACE || answers.REQUESTED_SURFACE);
171
+ if (!value || value === 'auto')
172
+ return value === 'auto' ? 'auto' : null;
173
+ if (['codex_in_app_browser', 'codex_chrome_extension', 'codex_computer_use', 'codex_app_plugin', 'structured_mcp', 'shell_or_api_diagnostic'].includes(value)) {
174
+ return value;
175
+ }
176
+ return null;
177
+ }
178
+ function uiRequiredFromAnswers(answers) {
179
+ const scope = stringValue(answers.QA_SCOPE || 'ui_e2e_only');
180
+ if (scope === 'api_e2e_only')
181
+ return false;
182
+ if (scope === 'all_available')
183
+ return Boolean(normalizeUnset(answers.TARGET_BASE_URL));
184
+ return ['ui_e2e_only', 'ui_and_api_e2e'].includes(scope);
185
+ }
186
+ function apiRequiredFromAnswers(answers) {
187
+ const scope = stringValue(answers.QA_SCOPE || 'ui_e2e_only');
188
+ return ['api_e2e_only', 'ui_and_api_e2e', 'all_available'].includes(scope);
189
+ }
190
+ function gptImage2RequiredFromAnswers(answers, prompt) {
191
+ const explicit = stringValue(answers.QA_VISUAL_REVIEW_IMAGEGEN_REQUIRED || answers.GPT_IMAGE_2_ANNOTATED_REVIEW_REQUIRED);
192
+ if (/^(yes|true|required|yes_gpt_image_2_annotated_review)$/i.test(explicit))
193
+ return true;
194
+ if (/^(no|false|not_required|none)$/i.test(explicit))
195
+ return false;
196
+ return /(gpt-image-2|gpt\s*image\s*2|imagegen|\$imagegen|annotated\s+review|callout|주석\s*이미지|콜아웃)/i.test(prompt);
197
+ }
198
+ function replayFingerprint(contract, steps) {
199
+ return [
200
+ contract.version,
201
+ contract.target.kind,
202
+ contract.target.url || 'no-url',
203
+ contract.auth.mode,
204
+ steps.map((step) => `${step.id}:${step.kind}:${step.target || ''}`).join('|')
205
+ ].join('::');
206
+ }
207
+ function firstUrl(value) {
208
+ return value.match(/https?:\/\/[^\s)\]}>,]+/i)?.[0] || null;
209
+ }
210
+ function normalizeUnset(value) {
211
+ const text = stringValue(value);
212
+ if (!text || /^(none|not_required|n\/a|na|unset|same_as_target)$/i.test(text))
213
+ return null;
214
+ return text;
215
+ }
216
+ function stringValue(value) {
217
+ return String(value || '').trim();
218
+ }
219
+ function boundedInteger(value, min, max, fallback) {
220
+ const n = Number.parseInt(String(value ?? ''), 10);
221
+ if (!Number.isFinite(n))
222
+ return fallback;
223
+ return Math.max(min, Math.min(max, n));
224
+ }
225
+ function isQaTargetKind(value) {
226
+ return ['local_web', 'public_web', 'signed_in_web', 'native_gui', 'cross_app_gui', 'structured_data', 'api_or_shell'].includes(value);
227
+ }
228
+ function isQaAuthMode(value) {
229
+ return ['not_required', 'runtime_ephemeral_credentials', 'existing_browser_profile', 'blocked_missing_credentials', 'unknown'].includes(value);
230
+ }
231
+ //# sourceMappingURL=qa-contract-v2.js.map
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import { exists, nowIso, readJson, readText, writeJsonAtomic } from '../fsx.js';
3
+ import { QA_ACTION_LEDGER_ARTIFACT, QA_CONTRACT_V2_ARTIFACT, QA_FINDING_LEDGER_ARTIFACT, QA_FIX_LEDGER_ARTIFACT, QA_GATE_V2_ARTIFACT, QA_OBSERVATION_LEDGER_ARTIFACT, QA_REPLAY_LEDGER_ARTIFACT, QA_SURFACE_SELECTION_ARTIFACT } from './qa-types.js';
4
+ export async function evaluateQaGateV2(dir) {
5
+ const contract = await readJson(path.join(dir, QA_CONTRACT_V2_ARTIFACT), null);
6
+ const surface = await readJson(path.join(dir, QA_SURFACE_SELECTION_ARTIFACT), null);
7
+ const actions = await readJsonl(path.join(dir, QA_ACTION_LEDGER_ARTIFACT));
8
+ const observations = await readJsonl(path.join(dir, QA_OBSERVATION_LEDGER_ARTIFACT));
9
+ const findings = await readJsonl(path.join(dir, QA_FINDING_LEDGER_ARTIFACT));
10
+ const fixes = await readJsonl(path.join(dir, QA_FIX_LEDGER_ARTIFACT));
11
+ const replays = await readJsonl(path.join(dir, QA_REPLAY_LEDGER_ARTIFACT));
12
+ const blockers = [];
13
+ const unverified = [];
14
+ if (!contract)
15
+ blockers.push('qa_contract_v2_missing');
16
+ if (!surface)
17
+ blockers.push('qa_surface_selection_missing');
18
+ const uiRequired = contract?.scope?.ui_required === true;
19
+ const findingRecords = findings.filter(isRuntimeRecord);
20
+ const fixRecords = fixes.filter(isRuntimeRecord);
21
+ const replayRecords = replays.filter(isRuntimeRecord);
22
+ const realActions = actions.filter(isRuntimeRecord).filter(isRealRecord);
23
+ const realObservations = observations.filter(isRuntimeRecord).filter(isRealRecord);
24
+ const unresolvedFindings = findingRecords.filter((record) => !['resolved', 'false_positive', 'deferred_nonfixable'].includes(String(record.status || record.data?.status || '')));
25
+ const appliedFixes = fixRecords.filter((record) => ['applied', 'verified'].includes(String(record.status || record.data?.status || '')));
26
+ const completedReplays = replayRecords.filter((record) => ['passed', 'completed', 'verified'].includes(String(record.status || record.data?.status || '')));
27
+ const sameFlowReplayComplete = appliedFixes.length === 0 || appliedFixes.every((fix) => {
28
+ const fixId = String(fix.fix_id || fix.data?.fix_id || fix.item_id || '');
29
+ const journey = String(fix.journey_fingerprint || fix.data?.journey_fingerprint || '');
30
+ return completedReplays.some((replay) => {
31
+ const replayFix = String(replay.fix_id || replay.data?.fix_id || replay.item_id || '');
32
+ const replayJourney = String(replay.journey_fingerprint || replay.data?.journey_fingerprint || '');
33
+ return (!fixId || replayFix === fixId) && (!journey || replayJourney === journey);
34
+ });
35
+ });
36
+ if (uiRequired && realActions.length === 0)
37
+ blockers.push('ui_required_but_real_action_count_zero');
38
+ if (uiRequired && realObservations.length === 0)
39
+ blockers.push('ui_required_but_observation_count_zero');
40
+ if (appliedFixes.length > 0 && !sameFlowReplayComplete)
41
+ blockers.push('applied_fix_without_same_flow_replay');
42
+ if (unresolvedFindings.length > 0)
43
+ blockers.push('unresolved_findings_remaining');
44
+ if (recordsContainSyntheticAsReal([...actions, ...observations, ...findings, ...fixes, ...replays]))
45
+ blockers.push('synthetic_or_mock_record_claimed_as_real');
46
+ if (surface?.selected_surface && uiRequired && ['structured_mcp', 'shell_or_api_diagnostic'].includes(surface.selected_surface)) {
47
+ blockers.push('ui_required_without_visual_surface');
48
+ }
49
+ if (surface && surface.visual_surface_required && realActions.length === 0) {
50
+ unverified.push(`${surface.selected_surface}_live_action_evidence_missing`);
51
+ }
52
+ const uniqueBlockers = [...new Set(blockers)];
53
+ const uniqueUnverified = [...new Set(unverified)];
54
+ const passed = uniqueBlockers.length === 0;
55
+ const result = {
56
+ schema: 'sks.qa-loop-gate.v2',
57
+ checked_at: nowIso(),
58
+ passed,
59
+ status: passed ? 'passed' : uniqueBlockers.some((item) => item.includes('missing') || item.includes('zero')) ? 'blocked' : 'failed',
60
+ mission_id: contract?.mission_id || surface?.mission_id || null,
61
+ selected_surface: surface?.selected_surface || null,
62
+ ui_required: uiRequired,
63
+ real_action_count: realActions.length,
64
+ observation_count: realObservations.length,
65
+ finding_count: findingRecords.length,
66
+ unresolved_finding_count: unresolvedFindings.length,
67
+ applied_fix_count: appliedFixes.length,
68
+ replay_count: completedReplays.length,
69
+ same_flow_replay_complete: sameFlowReplayComplete,
70
+ blockers: uniqueBlockers,
71
+ unverified: uniqueUnverified,
72
+ artifacts: {
73
+ contract_v2: QA_CONTRACT_V2_ARTIFACT,
74
+ surface_selection: QA_SURFACE_SELECTION_ARTIFACT,
75
+ action_ledger: QA_ACTION_LEDGER_ARTIFACT,
76
+ observation_ledger: QA_OBSERVATION_LEDGER_ARTIFACT,
77
+ finding_ledger: QA_FINDING_LEDGER_ARTIFACT,
78
+ fix_ledger: QA_FIX_LEDGER_ARTIFACT,
79
+ replay_ledger: QA_REPLAY_LEDGER_ARTIFACT
80
+ }
81
+ };
82
+ await writeJsonAtomic(path.join(dir, QA_GATE_V2_ARTIFACT), result);
83
+ return result;
84
+ }
85
+ async function readJsonl(file) {
86
+ if (!(await exists(file)))
87
+ return [];
88
+ const text = await readText(file, '');
89
+ const records = [];
90
+ for (const line of String(text).split(/\r?\n/)) {
91
+ if (!line.trim())
92
+ continue;
93
+ try {
94
+ const value = JSON.parse(line);
95
+ if (value && typeof value === 'object')
96
+ records.push(value);
97
+ }
98
+ catch {
99
+ records.push({ schema: 'sks.qa-loop.invalid-jsonl-record', status: 'invalid_json' });
100
+ }
101
+ }
102
+ return records;
103
+ }
104
+ function isRealRecord(record) {
105
+ const status = String(record.status || record.data?.status || '');
106
+ if (status === 'invalid_json')
107
+ return false;
108
+ if (record.synthetic === true || record.mock === true || record.fixture === true)
109
+ return false;
110
+ if (record.data?.synthetic === true || record.data?.mock === true || record.data?.fixture === true)
111
+ return false;
112
+ return ['real', 'passed', 'completed', 'observed', 'captured', 'verified', 'failed'].includes(status)
113
+ || record.real === true
114
+ || record.data?.real === true;
115
+ }
116
+ function isRuntimeRecord(record) {
117
+ const status = String(record.status || record.data?.status || '');
118
+ const kind = String(record.kind || record.data?.kind || '');
119
+ if (status === 'metadata' || kind === 'ledger_initialized')
120
+ return false;
121
+ if (record.schema === 'sks.qa-loop-ledger-init.v2')
122
+ return false;
123
+ return status !== 'invalid_json';
124
+ }
125
+ function recordsContainSyntheticAsReal(records) {
126
+ return records.some((record) => {
127
+ const synthetic = record.synthetic === true || record.mock === true || record.fixture === true || record.data?.synthetic === true || record.data?.mock === true || record.data?.fixture === true;
128
+ const claimedReal = record.real === true || record.data?.real === true || /real|live/i.test(String(record.status || record.data?.status || ''));
129
+ return synthetic && claimedReal;
130
+ });
131
+ }
132
+ //# sourceMappingURL=qa-gate-v2.js.map
@@ -0,0 +1,53 @@
1
+ import path from 'node:path';
2
+ import { appendJsonlBounded, nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { buildQaAuthDataSandboxPolicy, buildQaContractV2, buildQaJourneyGraphV2 } from './qa-contract-v2.js';
4
+ import { selectQaSurfaceForContract } from './qa-surface-router.js';
5
+ import { evaluateQaGateV2 } from './qa-gate-v2.js';
6
+ import { QA_ACTION_LEDGER_ARTIFACT, QA_ASSERTION_LEDGER_ARTIFACT, QA_AUTH_DATA_POLICY_ARTIFACT, QA_CONTRACT_V2_ARTIFACT, QA_FINDING_LEDGER_ARTIFACT, QA_FIX_LEDGER_ARTIFACT, QA_JOURNEY_GRAPH_ARTIFACT, QA_OBSERVATION_LEDGER_ARTIFACT, QA_REPLAY_LEDGER_ARTIFACT, QA_RUNTIME_EVENT_LEDGER_ARTIFACT, QA_SURFACE_SELECTION_ARTIFACT } from './qa-types.js';
7
+ export async function initializeQaRuntimeArtifacts(dir, legacyContract, options = {}) {
8
+ const contract = buildQaContractV2(legacyContract, options);
9
+ const surface = selectQaSurfaceForContract(contract);
10
+ const journey = buildQaJourneyGraphV2(contract);
11
+ const authDataPolicy = buildQaAuthDataSandboxPolicy(contract);
12
+ await writeJsonAtomic(path.join(dir, QA_CONTRACT_V2_ARTIFACT), contract);
13
+ await writeJsonAtomic(path.join(dir, QA_SURFACE_SELECTION_ARTIFACT), surface);
14
+ await writeJsonAtomic(path.join(dir, QA_JOURNEY_GRAPH_ARTIFACT), journey);
15
+ await writeJsonAtomic(path.join(dir, QA_AUTH_DATA_POLICY_ARTIFACT), authDataPolicy);
16
+ await appendRuntimeEvent(dir, 'qa-runtime.v2.initialized', {
17
+ selected_surface: surface.selected_surface,
18
+ target_kind: surface.target_kind,
19
+ ui_required: contract.scope.ui_required,
20
+ max_cycles: contract.runtime.max_cycles
21
+ });
22
+ await ensureLedgerPlaceholders(dir);
23
+ const gateV2 = await evaluateQaGateV2(dir);
24
+ return { contract, surface, journey, authDataPolicy, gateV2 };
25
+ }
26
+ async function appendRuntimeEvent(dir, kind, data) {
27
+ await appendJsonlBounded(path.join(dir, QA_RUNTIME_EVENT_LEDGER_ARTIFACT), {
28
+ schema: 'sks.qa-loop-runtime-event.v2',
29
+ ts: nowIso(),
30
+ kind,
31
+ status: 'completed',
32
+ data
33
+ });
34
+ }
35
+ async function ensureLedgerPlaceholders(dir) {
36
+ for (const artifact of [
37
+ QA_ACTION_LEDGER_ARTIFACT,
38
+ QA_OBSERVATION_LEDGER_ARTIFACT,
39
+ QA_ASSERTION_LEDGER_ARTIFACT,
40
+ QA_FINDING_LEDGER_ARTIFACT,
41
+ QA_FIX_LEDGER_ARTIFACT,
42
+ QA_REPLAY_LEDGER_ARTIFACT
43
+ ]) {
44
+ await appendJsonlBounded(path.join(dir, artifact), {
45
+ schema: 'sks.qa-loop-ledger-init.v2',
46
+ ts: nowIso(),
47
+ kind: 'ledger_initialized',
48
+ status: 'metadata',
49
+ real: false
50
+ });
51
+ }
52
+ }
53
+ //# sourceMappingURL=qa-runtime-artifacts.js.map
@@ -0,0 +1,114 @@
1
+ import { CODEX_APP_SERVER_DOC_URL, CODEX_CHROME_EXTENSION_DOC_URL, CODEX_COMPUTER_USE_DOC_URL, CODEX_IN_APP_BROWSER_DOC_URL } from '../routes.js';
2
+ import { nowIso } from '../fsx.js';
3
+ export function inferQaTargetKind(input) {
4
+ const prompt = String(input.prompt || '').toLowerCase();
5
+ const url = String(input.targetUrl || '').trim();
6
+ if (/(native|desktop|macos|windows|os\s*setting|system settings|finder|xcode|appname|@computer|@appname|네이티브|데스크톱|운영체제|설정)/i.test(prompt)) {
7
+ return /(multi[- ]?app|cross[- ]?app|여러\s*앱|앱\s*간)/i.test(prompt) ? 'cross_app_gui' : 'native_gui';
8
+ }
9
+ if (input.authMode === 'existing_browser_profile' || /(signed[- ]?in|logged[- ]?in|cookie|profile|extension|internal tool|sso|로그인|쿠키|프로필|확장)/i.test(prompt)) {
10
+ return 'signed_in_web';
11
+ }
12
+ if (/^(?:https?:\/\/)?(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:\/|$)/i.test(url) || /^file:\/\//i.test(url))
13
+ return 'local_web';
14
+ if (/^https?:\/\//i.test(url))
15
+ return 'public_web';
16
+ if (/(database|mcp|plugin|spreadsheet|notion|gmail|slack|data sync|structured data|데이터|플러그인)/i.test(prompt))
17
+ return 'structured_data';
18
+ if (/(api|endpoint|cli|shell|command|엔드포인트|명령어)/i.test(prompt))
19
+ return 'api_or_shell';
20
+ return 'local_web';
21
+ }
22
+ export function selectQaSurface(input) {
23
+ const uiRequired = input.uiRequired !== false;
24
+ const authMode = input.authMode || inferAuthMode(input.prompt || '');
25
+ const targetKind = input.targetKind || inferQaTargetKind({ prompt: input.prompt ?? null, targetUrl: input.targetUrl ?? null, authMode });
26
+ const requested = input.requestedSurface && input.requestedSurface !== 'auto' ? input.requestedSurface : null;
27
+ const policySurface = surfaceForKind(targetKind, uiRequired, authMode);
28
+ const selected = requested || policySurface;
29
+ const reason = requested
30
+ ? `operator_requested_${requested}_after_policy_kind_${targetKind}`
31
+ : reasonForSelection(policySurface, targetKind, authMode);
32
+ return {
33
+ schema: 'sks.qa-loop-surface-selection.v2',
34
+ selected_at: nowIso(),
35
+ mission_id: input.missionId || null,
36
+ selected_surface: selected,
37
+ target_kind: targetKind,
38
+ auth_mode: authMode,
39
+ ui_required: uiRequired,
40
+ reason,
41
+ docs_url: docsUrlForSurface(selected),
42
+ alternatives: alternativesFor(policySurface, selected, targetKind, authMode),
43
+ structured_data_first: targetKind === 'structured_data',
44
+ visual_surface_required: uiRequired && !['structured_mcp', 'shell_or_api_diagnostic'].includes(selected)
45
+ };
46
+ }
47
+ export function selectQaSurfaceForContract(contract) {
48
+ return selectQaSurface({
49
+ missionId: contract.mission_id,
50
+ prompt: contract.prompt,
51
+ targetUrl: contract.target.url,
52
+ uiRequired: contract.scope.ui_required,
53
+ authMode: contract.auth.mode,
54
+ requestedSurface: contract.cli.requested_surface,
55
+ targetKind: contract.target.kind
56
+ });
57
+ }
58
+ export function inferAuthMode(prompt) {
59
+ if (/(cookie|profile|existing tab|extension|signed[- ]?in session|browser state|쿠키|프로필|세션)/i.test(prompt))
60
+ return 'existing_browser_profile';
61
+ if (/(login|log in|signin|sign in|auth|credential|sso|로그인|인증|계정)/i.test(prompt))
62
+ return 'blocked_missing_credentials';
63
+ return 'not_required';
64
+ }
65
+ function surfaceForKind(kind, uiRequired, authMode) {
66
+ if (!uiRequired && kind === 'structured_data')
67
+ return 'structured_mcp';
68
+ if (!uiRequired)
69
+ return 'shell_or_api_diagnostic';
70
+ if (kind === 'signed_in_web' || authMode === 'existing_browser_profile' || authMode === 'runtime_ephemeral_credentials')
71
+ return 'codex_chrome_extension';
72
+ if (kind === 'native_gui' || kind === 'cross_app_gui')
73
+ return 'codex_computer_use';
74
+ if (kind === 'structured_data')
75
+ return 'structured_mcp';
76
+ return 'codex_in_app_browser';
77
+ }
78
+ function reasonForSelection(surface, kind, authMode) {
79
+ if (surface === 'codex_in_app_browser')
80
+ return `local_or_public_unauthenticated_web_kind_${kind}`;
81
+ if (surface === 'codex_chrome_extension')
82
+ return `signed_in_browser_state_required_auth_${authMode}`;
83
+ if (surface === 'codex_computer_use')
84
+ return `native_or_cross_app_gui_kind_${kind}`;
85
+ if (surface === 'structured_mcp')
86
+ return 'structured_data_operation_first_then_visual_verify_if_needed';
87
+ return 'api_or_shell_diagnostic_no_ui_surface_required';
88
+ }
89
+ function docsUrlForSurface(surface) {
90
+ if (surface === 'codex_in_app_browser')
91
+ return CODEX_IN_APP_BROWSER_DOC_URL;
92
+ if (surface === 'codex_chrome_extension')
93
+ return CODEX_CHROME_EXTENSION_DOC_URL;
94
+ if (surface === 'codex_computer_use')
95
+ return CODEX_COMPUTER_USE_DOC_URL;
96
+ return CODEX_APP_SERVER_DOC_URL;
97
+ }
98
+ function alternativesFor(policySurface, selected, kind, authMode) {
99
+ const choices = ['codex_in_app_browser', 'codex_chrome_extension', 'codex_computer_use', 'structured_mcp', 'shell_or_api_diagnostic'];
100
+ return choices.map((surface) => {
101
+ if (surface === selected)
102
+ return { surface, status: 'selected', reason: surface === policySurface ? 'policy_default' : 'operator_override' };
103
+ if (surface === policySurface)
104
+ return { surface, status: 'fallback_candidate', reason: 'policy_default_not_selected_due_override' };
105
+ if (surface === 'codex_chrome_extension' && authMode === 'not_required' && (kind === 'local_web' || kind === 'public_web')) {
106
+ return { surface, status: 'not_applicable', reason: 'no_signed_in_browser_state_required' };
107
+ }
108
+ if (surface === 'codex_computer_use' && !['native_gui', 'cross_app_gui'].includes(kind)) {
109
+ return { surface, status: 'blocked_by_policy', reason: 'not_native_or_cross_app_gui' };
110
+ }
111
+ return { surface, status: 'not_applicable', reason: `not_required_for_${kind}` };
112
+ });
113
+ }
114
+ //# sourceMappingURL=qa-surface-router.js.map
@@ -0,0 +1,18 @@
1
+ export const QA_LOOP_CONTRACT_VERSION = 2;
2
+ export const DEFAULT_QA_MAX_CYCLES = 5;
3
+ export const QA_LOOP_V2_DIR = 'qa-loop';
4
+ export const QA_CONTRACT_V2_ARTIFACT = 'qa-loop/qa-contract-v2.json';
5
+ export const QA_SURFACE_SELECTION_ARTIFACT = 'qa-loop/qa-surface-selection.json';
6
+ export const QA_JOURNEY_GRAPH_ARTIFACT = 'qa-loop/qa-journey-graph.json';
7
+ export const QA_ACTION_LEDGER_ARTIFACT = 'qa-loop/action-ledger.jsonl';
8
+ export const QA_OBSERVATION_LEDGER_ARTIFACT = 'qa-loop/observation-ledger.jsonl';
9
+ export const QA_ASSERTION_LEDGER_ARTIFACT = 'qa-loop/assertion-ledger.jsonl';
10
+ export const QA_FINDING_LEDGER_ARTIFACT = 'qa-loop/finding-ledger.jsonl';
11
+ export const QA_FIX_LEDGER_ARTIFACT = 'qa-loop/fix-ledger.jsonl';
12
+ export const QA_REPLAY_LEDGER_ARTIFACT = 'qa-loop/replay-ledger.jsonl';
13
+ export const QA_RUNTIME_EVENT_LEDGER_ARTIFACT = 'qa-loop/runtime-events.jsonl';
14
+ export const QA_GATE_V2_ARTIFACT = 'qa-loop/qa-gate-v2.json';
15
+ export const QA_LIVE_SESSION_ARTIFACT = 'qa-loop/live-session.json';
16
+ export const QA_DEV_SERVER_ARTIFACT = 'qa-loop/dev-server.json';
17
+ export const QA_AUTH_DATA_POLICY_ARTIFACT = 'qa-loop/auth-data-sandbox-policy.json';
18
+ //# sourceMappingURL=qa-types.js.map