@knights-eye-chess/ai-analysis 0.0.0-stage → 0.1.0-dev.17
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/LICENSE +235 -0
- package/LICENSING.md +26 -0
- package/NOTICE +5 -0
- package/NPM-PUBLISHING.md +28 -0
- package/README.md +139 -2
- package/SOURCE-ORIGIN.json +16 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/build.mjs +22 -0
- package/dist/answer-pipeline.test.mjs +375 -0
- package/dist/chunk-PLKNJRPF.js +10662 -0
- package/dist/chunk-XQDZJOYD.js +2515 -0
- package/dist/index.js +146 -0
- package/dist/owner-tests/chess-reasoning-compact-search-preparation.test.mjs +963 -0
- package/dist/owner-tests/mechanism-encoding.test.mjs +529 -0
- package/dist/owner-tests/move-mechanism-facts.test.mjs +396 -0
- package/dist/owner-tests/pipeline-host-preparation.test.mjs +10556 -0
- package/dist/owner-tests/position-preparation.test.mjs +625 -0
- package/dist/owner-tests/preparation-consolidation.test.mjs +1052 -0
- package/dist/owner-tests/prose-reference-admission.test.mjs +1770 -0
- package/dist/owner-tests/score-comparison-notices.test.mjs +240 -0
- package/dist/owner-tests/simple-engine-presentation.test.mjs +233 -0
- package/dist/owner-tests/tool-input-loop-guard.test.mjs +78 -0
- package/dist/research.js +2274 -0
- package/dist/runtime-closure.json +103 -0
- package/dist/shared-runtime-closure.json +39 -0
- package/dist/shared.js +152 -0
- package/dist/types/ai-request-failure.d.ts +14 -0
- package/dist/types/ai-request-limits.d.ts +4 -0
- package/dist/types/ai-routing-diagnostics.d.ts +14 -0
- package/dist/types/chess-reasoning/ablation-adaptive-evidence-loop.d.ts +127 -0
- package/dist/types/chess-reasoning/ablation-adaptive-runtime.d.ts +178 -0
- package/dist/types/chess-reasoning/ablation-direct-synthesis.d.ts +1030 -0
- package/dist/types/chess-reasoning/ablation-evidence-bundle.d.ts +185 -0
- package/dist/types/chess-reasoning/ablation-evidence-capture.d.ts +37 -0
- package/dist/types/chess-reasoning/ablation-lean-direct-runtime.d.ts +193 -0
- package/dist/types/chess-reasoning/ablation-model-facing-evidence.d.ts +38 -0
- package/dist/types/chess-reasoning/ablation-transport-correction.d.ts +13 -0
- package/dist/types/chess-reasoning/ai-sdk-runtime-support.d.ts +93 -0
- package/dist/types/chess-reasoning/answer-host-contracts.d.ts +59 -0
- package/dist/types/chess-reasoning/answer-model-connector.d.ts +68 -0
- package/dist/types/chess-reasoning/answer-pipeline-exports.d.ts +98 -0
- package/dist/types/chess-reasoning/answer-pipeline.d.ts +41 -0
- package/dist/types/chess-reasoning/answer-reference-validation.d.ts +40 -0
- package/dist/types/chess-reasoning/answer-tool-host.d.ts +8 -0
- package/dist/types/chess-reasoning/branch-expansion-policy.d.ts +13 -0
- package/dist/types/chess-reasoning/branch-expansion.d.ts +39 -0
- package/dist/types/chess-reasoning/causal-claim-checks.d.ts +21 -0
- package/dist/types/chess-reasoning/chess-reference-dictionary.d.ts +13 -0
- package/dist/types/chess-reasoning/classification-question-target.d.ts +22 -0
- package/dist/types/chess-reasoning/classification-reassessment.d.ts +31 -0
- package/dist/types/chess-reasoning/compact-search-preparation.d.ts +7 -0
- package/dist/types/chess-reasoning/concept-registry.d.ts +99 -0
- package/dist/types/chess-reasoning/conversation-context.d.ts +12 -0
- package/dist/types/chess-reasoning/engine-search-failure.d.ts +7 -0
- package/dist/types/chess-reasoning/evidence-citation-normalization.d.ts +9 -0
- package/dist/types/chess-reasoning/evidence-licenses.d.ts +57 -0
- package/dist/types/chess-reasoning/evidence-selector-contract.d.ts +5 -0
- package/dist/types/chess-reasoning/existing-analysis-context.d.ts +104 -0
- package/dist/types/chess-reasoning/fact-observation-provider.d.ts +15 -0
- package/dist/types/chess-reasoning/fact-provider.d.ts +111 -0
- package/dist/types/chess-reasoning/fresh-engine-pv-witnesses.d.ts +67 -0
- package/dist/types/chess-reasoning/historical-prose.d.ts +23 -0
- package/dist/types/chess-reasoning/idea-graph.d.ts +34 -0
- package/dist/types/chess-reasoning/initial-search-plan.d.ts +37 -0
- package/dist/types/chess-reasoning/lossless-evidence-sharing.d.ts +18 -0
- package/dist/types/chess-reasoning/matched-comparison.d.ts +10 -0
- package/dist/types/chess-reasoning/material-accounting.d.ts +80 -0
- package/dist/types/chess-reasoning/material-format.d.ts +4 -0
- package/dist/types/chess-reasoning/mechanical-fact-provider.d.ts +33 -0
- package/dist/types/chess-reasoning/mechanical-ray-facts.d.ts +19 -0
- package/dist/types/chess-reasoning/mechanism-presentation.d.ts +9 -0
- package/dist/types/chess-reasoning/move-mechanism-facts.d.ts +45 -0
- package/dist/types/chess-reasoning/native-answer-connector.d.ts +4 -0
- package/dist/types/chess-reasoning/opening-source-contracts.d.ts +54 -0
- package/dist/types/chess-reasoning/opening-source-credits.d.ts +16 -0
- package/dist/types/chess-reasoning/opponent-threat-preparation.d.ts +7 -0
- package/dist/types/chess-reasoning/piece-identities.d.ts +11 -0
- package/dist/types/chess-reasoning/poc-contracts.d.ts +1026 -0
- package/dist/types/chess-reasoning/poc-hypotheses.d.ts +74 -0
- package/dist/types/chess-reasoning/poc-move-witnesses.d.ts +13 -0
- package/dist/types/chess-reasoning/poc-observations.d.ts +23 -0
- package/dist/types/chess-reasoning/poc-piece-refs.d.ts +12 -0
- package/dist/types/chess-reasoning/poc-plan-model.d.ts +45 -0
- package/dist/types/chess-reasoning/poc-semantic-text.d.ts +8 -0
- package/dist/types/chess-reasoning/poc-tools.d.ts +81 -0
- package/dist/types/chess-reasoning/portable-answer-runtime.d.ts +291 -0
- package/dist/types/chess-reasoning/position-analysis-contracts.d.ts +31 -0
- package/dist/types/chess-reasoning/position-analysis-scope.d.ts +3 -0
- package/dist/types/chess-reasoning/preparation-consolidation.d.ts +21 -0
- package/dist/types/chess-reasoning/production-observation-provider.d.ts +16 -0
- package/dist/types/chess-reasoning/question-routing.d.ts +326 -0
- package/dist/types/chess-reasoning/recorded-main-line.d.ts +37 -0
- package/dist/types/chess-reasoning/recorded-position-tools.d.ts +11 -0
- package/dist/types/chess-reasoning/recorded-reference-context.d.ts +13 -0
- package/dist/types/chess-reasoning/recorded-selected-classification.d.ts +36 -0
- package/dist/types/chess-reasoning/reference-limits.d.ts +2 -0
- package/dist/types/chess-reasoning/research-exports.d.ts +197 -0
- package/dist/types/chess-reasoning/saved-score-presentation.d.ts +14 -0
- package/dist/types/chess-reasoning/score-comparison-notices.d.ts +33 -0
- package/dist/types/chess-reasoning/server-simple-policy.d.ts +31 -0
- package/dist/types/chess-reasoning/simple-agent-config.d.ts +73 -0
- package/dist/types/chess-reasoning/simple-board-analysis.d.ts +85 -0
- package/dist/types/chess-reasoning/simple-compact-evidence.d.ts +7 -0
- package/dist/types/chess-reasoning/simple-completion.d.ts +23 -0
- package/dist/types/chess-reasoning/simple-engine-presentation.d.ts +3 -0
- package/dist/types/chess-reasoning/simple-evidence-preparation.d.ts +17 -0
- package/dist/types/chess-reasoning/simple-evidence-view.d.ts +17 -0
- package/dist/types/chess-reasoning/simple-explanation-coverage.d.ts +90 -0
- package/dist/types/chess-reasoning/simple-fact-delivery.d.ts +43 -0
- package/dist/types/chess-reasoning/simple-fallback-premise.d.ts +5 -0
- package/dist/types/chess-reasoning/simple-prose-references.d.ts +108 -0
- package/dist/types/chess-reasoning/simple-runtime-version.d.ts +1 -0
- package/dist/types/chess-reasoning/stable.d.ts +2 -0
- package/dist/types/chess-reasoning/tool-input-loop-guard.d.ts +5 -0
- package/dist/types/chess-reasoning/types.d.ts +180 -0
- package/dist/types/chess-reasoning/visible-support-lines.d.ts +12 -0
- package/dist/types/chess-reasoning/wikibooks-index.d.ts +30 -0
- package/dist/types/chess-reasoning/wikibooks-tool-context.d.ts +10 -0
- package/dist/types/player-perspective.d.ts +16 -0
- package/dist/types/shared-exports.d.ts +22 -0
- package/docs/API.md +82 -0
- package/docs/OWNERSHIP-ADOPTION.md +35 -0
- package/docs/OWNERSHIP-DRIFT.json +55 -0
- package/docs/RESEARCH-API.md +28 -0
- package/licenses/third-party/INVENTORY.json +202 -0
- package/licenses/third-party/ai-7.0.85/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--gateway-4.0.69/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--provider-4.0.9/LICENSE +13 -0
- package/licenses/third-party/ai-sdk--provider-utils-5.0.34/APACHE-2.0.txt +73 -0
- package/licenses/third-party/ai-sdk--provider-utils-5.0.34/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
- package/licenses/third-party/chess.js-1.4.0/LICENSE +23 -0
- package/licenses/third-party/eventsource-parser-3.1.1/LICENSE +21 -0
- package/licenses/third-party/json-schema-0.4.0/LICENSE +195 -0
- package/licenses/third-party/standard-schema--spec-1.1.0/LICENSE +21 -0
- package/licenses/third-party/types--json-schema-7.0.15/LICENSE +21 -0
- package/licenses/third-party/types--node-22.19.19/LICENSE +21 -0
- package/licenses/third-party/undici-7.30.0/LICENSE +21 -0
- package/licenses/third-party/undici-7.30.0/lib/web/fetch/LICENSE +21 -0
- package/licenses/third-party/undici-types-6.21.0/LICENSE +21 -0
- package/licenses/third-party/vercel--oidc-3.2.0/LICENSE +202 -0
- package/licenses/third-party/workflow--serde-4.1.0/APACHE-2.0.txt +73 -0
- package/licenses/third-party/workflow--serde-4.1.0/LICENSE.md +3 -0
- package/licenses/third-party/zod-4.6.5/LICENSE +21 -0
- package/npm-release.json +5 -0
- package/package.json +72 -4
- package/scripts/check-release-tooling.mjs +22 -0
- package/scripts/pack.mjs +68 -0
- package/scripts/publish-reviewed.mjs +85 -0
- package/scripts/release-tooling.json +13 -0
- package/scripts/update-dependency.mjs +44 -0
- package/scripts/verify-release.mjs +52 -0
- package/src/ai-request-failure.ts +34 -0
- package/src/ai-request-limits.ts +4 -0
- package/src/ai-routing-diagnostics.ts +33 -0
- package/src/chess-reasoning/ablation-adaptive-evidence-loop.ts +515 -0
- package/src/chess-reasoning/ablation-adaptive-runtime.ts +924 -0
- package/src/chess-reasoning/ablation-direct-synthesis.ts +1128 -0
- package/src/chess-reasoning/ablation-evidence-bundle.ts +726 -0
- package/src/chess-reasoning/ablation-evidence-capture.ts +513 -0
- package/src/chess-reasoning/ablation-lean-direct-runtime.ts +670 -0
- package/src/chess-reasoning/ablation-model-facing-evidence.ts +212 -0
- package/src/chess-reasoning/ablation-transport-correction.ts +106 -0
- package/src/chess-reasoning/ai-sdk-runtime-support.ts +381 -0
- package/src/chess-reasoning/answer-host-contracts.ts +10 -0
- package/src/chess-reasoning/answer-model-connector.ts +83 -0
- package/src/chess-reasoning/answer-pipeline-exports.ts +98 -0
- package/src/chess-reasoning/answer-pipeline.ts +195 -0
- package/src/chess-reasoning/answer-reference-validation.ts +124 -0
- package/src/chess-reasoning/answer-tool-host.ts +10 -0
- package/src/chess-reasoning/branch-expansion-policy.ts +32 -0
- package/src/chess-reasoning/branch-expansion.ts +80 -0
- package/src/chess-reasoning/causal-claim-checks.ts +256 -0
- package/src/chess-reasoning/chess-reference-dictionary.ts +21 -0
- package/src/chess-reasoning/classification-question-target.ts +30 -0
- package/src/chess-reasoning/classification-reassessment.ts +34 -0
- package/src/chess-reasoning/compact-search-preparation.ts +125 -0
- package/src/chess-reasoning/concept-registry.ts +275 -0
- package/src/chess-reasoning/conversation-context.ts +29 -0
- package/src/chess-reasoning/engine-search-failure.ts +11 -0
- package/src/chess-reasoning/evidence-citation-normalization.ts +15 -0
- package/src/chess-reasoning/evidence-licenses.ts +166 -0
- package/src/chess-reasoning/evidence-selector-contract.ts +5 -0
- package/src/chess-reasoning/existing-analysis-context.ts +544 -0
- package/src/chess-reasoning/fact-observation-provider.ts +134 -0
- package/src/chess-reasoning/fact-provider.ts +250 -0
- package/src/chess-reasoning/fresh-engine-pv-witnesses.ts +128 -0
- package/src/chess-reasoning/historical-prose.ts +116 -0
- package/src/chess-reasoning/idea-graph.ts +224 -0
- package/src/chess-reasoning/initial-search-plan.ts +115 -0
- package/src/chess-reasoning/lossless-evidence-sharing.ts +135 -0
- package/src/chess-reasoning/matched-comparison.ts +40 -0
- package/src/chess-reasoning/material-accounting.ts +55 -0
- package/src/chess-reasoning/material-format.ts +47 -0
- package/src/chess-reasoning/mechanical-fact-provider.ts +1602 -0
- package/src/chess-reasoning/mechanical-ray-facts.ts +123 -0
- package/src/chess-reasoning/mechanism-presentation.ts +94 -0
- package/src/chess-reasoning/move-mechanism-facts.ts +185 -0
- package/src/chess-reasoning/native-answer-connector.ts +13 -0
- package/src/chess-reasoning/opening-source-contracts.ts +47 -0
- package/src/chess-reasoning/opening-source-credits.ts +53 -0
- package/src/chess-reasoning/opponent-threat-preparation.ts +98 -0
- package/src/chess-reasoning/piece-identities.ts +159 -0
- package/src/chess-reasoning/poc-contracts.ts +1097 -0
- package/src/chess-reasoning/poc-hypotheses.ts +759 -0
- package/src/chess-reasoning/poc-move-witnesses.ts +170 -0
- package/src/chess-reasoning/poc-observations.ts +611 -0
- package/src/chess-reasoning/poc-piece-refs.ts +62 -0
- package/src/chess-reasoning/poc-plan-model.ts +345 -0
- package/src/chess-reasoning/poc-semantic-text.ts +10 -0
- package/src/chess-reasoning/poc-tools.ts +978 -0
- package/src/chess-reasoning/portable-answer-runtime.ts +684 -0
- package/src/chess-reasoning/position-analysis-contracts.ts +88 -0
- package/src/chess-reasoning/position-analysis-scope.ts +20 -0
- package/src/chess-reasoning/preparation-consolidation.ts +91 -0
- package/src/chess-reasoning/production-observation-provider.ts +41 -0
- package/src/chess-reasoning/question-routing.ts +332 -0
- package/src/chess-reasoning/recorded-main-line.ts +55 -0
- package/src/chess-reasoning/recorded-position-tools.ts +78 -0
- package/src/chess-reasoning/recorded-reference-context.ts +44 -0
- package/src/chess-reasoning/recorded-selected-classification.ts +54 -0
- package/src/chess-reasoning/reference-limits.ts +2 -0
- package/src/chess-reasoning/research-exports.ts +197 -0
- package/src/chess-reasoning/saved-score-presentation.ts +6 -0
- package/src/chess-reasoning/score-comparison-notices.ts +115 -0
- package/src/chess-reasoning/server-simple-policy.ts +75 -0
- package/src/chess-reasoning/simple-agent-config.ts +58 -0
- package/src/chess-reasoning/simple-board-analysis.ts +57 -0
- package/src/chess-reasoning/simple-compact-evidence.ts +79 -0
- package/src/chess-reasoning/simple-completion.ts +37 -0
- package/src/chess-reasoning/simple-engine-presentation.ts +95 -0
- package/src/chess-reasoning/simple-evidence-preparation.ts +32 -0
- package/src/chess-reasoning/simple-evidence-view.ts +69 -0
- package/src/chess-reasoning/simple-explanation-coverage.ts +118 -0
- package/src/chess-reasoning/simple-fact-delivery.ts +52 -0
- package/src/chess-reasoning/simple-fallback-premise.ts +65 -0
- package/src/chess-reasoning/simple-prose-references.ts +528 -0
- package/src/chess-reasoning/simple-runtime-version.ts +2 -0
- package/src/chess-reasoning/stable.ts +18 -0
- package/src/chess-reasoning/tool-input-loop-guard.ts +38 -0
- package/src/chess-reasoning/types.ts +195 -0
- package/src/chess-reasoning/visible-support-lines.ts +44 -0
- package/src/chess-reasoning/wikibooks-index.ts +56 -0
- package/src/chess-reasoning/wikibooks-tool-context.ts +91 -0
- package/src/player-perspective.ts +41 -0
- package/src/shared-exports.ts +23 -0
- package/test/answer-pipeline.test.mjs +375 -0
- package/test/owner/chess-reasoning-compact-search-preparation.test.mjs +111 -0
- package/test/owner/mechanism-encoding.test.mjs +78 -0
- package/test/owner/move-mechanism-facts.test.mjs +105 -0
- package/test/owner/pipeline-host-preparation.test.mjs +115 -0
- package/test/owner/position-preparation.test.mjs +22 -0
- package/test/owner/preparation-consolidation.test.mjs +74 -0
- package/test/owner/prose-reference-admission.test.mjs +26 -0
- package/test/owner/score-comparison-notices.test.mjs +116 -0
- package/test/owner/simple-engine-presentation.test.mjs +50 -0
- package/test/owner/tool-input-loop-guard.test.mjs +25 -0
- package/test-types.mts +23 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
import { Chess, type Square } from "chess.js";
|
|
2
|
+
import { PARTICIPANT_ROLES, REFERENCE_DOMAINS, participant } from "./concept-registry";
|
|
3
|
+
import { EVIDENCE_LICENSE, type EvidenceLicense, validateEvidenceLicenseClaims, withEvidenceLicenses } from "./evidence-licenses";
|
|
4
|
+
import { factProofStrength, isChessFactAuthority, type ChessFactAuthority } from "./fact-provider";
|
|
5
|
+
import {
|
|
6
|
+
REASONING_POC_VERSION,
|
|
7
|
+
reasoningContextHash,
|
|
8
|
+
type ObservationProvider,
|
|
9
|
+
type ReasoningEvidenceRecord,
|
|
10
|
+
type ReasoningObservation,
|
|
11
|
+
type ReasoningObservationSet,
|
|
12
|
+
type ReasoningPositionContext,
|
|
13
|
+
type ReasoningRunControls,
|
|
14
|
+
} from "./poc-contracts";
|
|
15
|
+
import { createPocIdentityTimeline, pieceIdAt } from "./poc-piece-refs";
|
|
16
|
+
import { stableHash } from "./stable";
|
|
17
|
+
import type { SemanticValue } from "./types";
|
|
18
|
+
|
|
19
|
+
export const EXISTING_ANALYSIS_CONTEXT_VERSION = "existing-analysis-context/2" as const;
|
|
20
|
+
export const LEGACY_EXISTING_ANALYSIS_CONTEXT_VERSION = "existing-analysis-context/1" as const;
|
|
21
|
+
export const CANONICAL_EDGE_ANALYSIS_SNAPSHOT_VERSION = "canonical-edge-analysis-snapshot/1" as const;
|
|
22
|
+
export const EXISTING_ANALYSIS_RECORD_VERSION = "existing-analysis-record/1" as const;
|
|
23
|
+
export const MATERIAL_OFFER_SET_VERSION = "classifier-material-offer-set/1" as const;
|
|
24
|
+
export const EXISTING_ANALYSIS_OBSERVATION_PROVIDER_ID = "knights-eye/existing-analysis-context" as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Development/evaluation views over one identical canonical context snapshot.
|
|
28
|
+
* These are ablation controls, not user-query kinds or alternative providers.
|
|
29
|
+
*/
|
|
30
|
+
export const EXISTING_ANALYSIS_CONTEXT_MODE = Object.freeze({
|
|
31
|
+
HIDDEN: "hidden",
|
|
32
|
+
LABEL_ONLY: "label-only",
|
|
33
|
+
DIAGNOSTICS_ONLY: "diagnostics-only",
|
|
34
|
+
FULL: "full",
|
|
35
|
+
} as const);
|
|
36
|
+
|
|
37
|
+
export type ExistingAnalysisContextMode = typeof EXISTING_ANALYSIS_CONTEXT_MODE[keyof typeof EXISTING_ANALYSIS_CONTEXT_MODE];
|
|
38
|
+
|
|
39
|
+
export const ANALYSIS_CONTEXT_PROVENANCE_TIER = Object.freeze({
|
|
40
|
+
RAW_ANALYSIS: "raw-analysis",
|
|
41
|
+
LEGACY_HEURISTIC: "legacy-heuristic-diagnostic",
|
|
42
|
+
REVIEWED_SEMANTIC: "reviewed-semantic-state",
|
|
43
|
+
POLICY_LABEL: "policy-derived-label",
|
|
44
|
+
} as const);
|
|
45
|
+
|
|
46
|
+
export type AnalysisContextProvenanceTier = typeof ANALYSIS_CONTEXT_PROVENANCE_TIER[keyof typeof ANALYSIS_CONTEXT_PROVENANCE_TIER];
|
|
47
|
+
|
|
48
|
+
export type MaterialOfferCandidateSummary = {
|
|
49
|
+
captureUci: string;
|
|
50
|
+
offeredSquare: string;
|
|
51
|
+
offeredPiece: string;
|
|
52
|
+
nominalOffer: number;
|
|
53
|
+
voluntaryOfferCap: number;
|
|
54
|
+
bestImmediateRecoveryNet: number;
|
|
55
|
+
qualifying: boolean;
|
|
56
|
+
acceptanceLine: string[];
|
|
57
|
+
persistent: boolean | null;
|
|
58
|
+
conversionGain: number | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type MaterialOfferSet = {
|
|
62
|
+
version: typeof MATERIAL_OFFER_SET_VERSION;
|
|
63
|
+
status: "complete" | "unavailable";
|
|
64
|
+
offers: MaterialOfferCandidateSummary[] | null;
|
|
65
|
+
screenedCandidates: MaterialOfferCandidateSummary[];
|
|
66
|
+
classifierVersion: number;
|
|
67
|
+
detector: "classifier-sacrifice-offer-tracker";
|
|
68
|
+
policy: string;
|
|
69
|
+
horizon: {
|
|
70
|
+
verificationDepth: number | null;
|
|
71
|
+
materialWindowPlies: number | null;
|
|
72
|
+
principalVariationPlies: number;
|
|
73
|
+
};
|
|
74
|
+
limitations: string[];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type ExistingAnalysisRecord = {
|
|
78
|
+
version: typeof EXISTING_ANALYSIS_RECORD_VERSION;
|
|
79
|
+
id: string;
|
|
80
|
+
kind: string;
|
|
81
|
+
scope: "selected-edge" | "path-window";
|
|
82
|
+
authority: ChessFactAuthority;
|
|
83
|
+
licenses: EvidenceLicense[];
|
|
84
|
+
summary: string;
|
|
85
|
+
data: Record<string, SemanticValue>;
|
|
86
|
+
coverage: { complete: boolean; covered: string[]; unresolved: string[] };
|
|
87
|
+
provenance: {
|
|
88
|
+
tier: AnalysisContextProvenanceTier;
|
|
89
|
+
provider: string;
|
|
90
|
+
version: string;
|
|
91
|
+
derivedFromIds: string[];
|
|
92
|
+
};
|
|
93
|
+
replay: Record<string, SemanticValue> | null;
|
|
94
|
+
limitations: string[];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type ExistingAnalysisContext = {
|
|
98
|
+
version: typeof EXISTING_ANALYSIS_CONTEXT_VERSION;
|
|
99
|
+
materialOffers: MaterialOfferSet;
|
|
100
|
+
records: ExistingAnalysisRecord[];
|
|
101
|
+
coverage: {
|
|
102
|
+
complete: boolean;
|
|
103
|
+
includedKinds: string[];
|
|
104
|
+
omitted: Array<{ kind: string; reason: "relevance" | "budget" | "unavailable" }>;
|
|
105
|
+
unavailableKinds: string[];
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
110
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
111
|
+
? value as Record<string, unknown>
|
|
112
|
+
: null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function finiteNumber(value: unknown, fallback = 0) {
|
|
116
|
+
const parsed = Number(value);
|
|
117
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function nullableNumber(value: unknown) {
|
|
121
|
+
return value == null || !Number.isFinite(Number(value)) ? null : Number(value);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function boundedText(value: unknown, maximum = 240) {
|
|
125
|
+
return String(value ?? "").slice(0, maximum);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function moveList(value: unknown, limit = 12) {
|
|
129
|
+
return Array.isArray(value)
|
|
130
|
+
? value.map(String).filter((move) => /^[a-h][1-8][a-h][1-8][qrbn]?$/.test(move)).slice(0, limit)
|
|
131
|
+
: [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function semanticValue(value: unknown, depth = 0): SemanticValue | null {
|
|
135
|
+
if (value == null || typeof value === "string" || typeof value === "boolean") return value == null ? null : typeof value === "string" ? value.slice(0, 500) : value;
|
|
136
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
137
|
+
if (depth >= 6) return null;
|
|
138
|
+
if (Array.isArray(value)) return value.slice(0, 16).map((item) => semanticValue(item, depth + 1));
|
|
139
|
+
const item = record(value);
|
|
140
|
+
if (!item) return null;
|
|
141
|
+
return Object.fromEntries(Object.entries(item).slice(0, 32).map(([key, itemValue]) => [key.slice(0, 80), semanticValue(itemValue, depth + 1)]));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function semanticRecord(value: unknown): Record<string, SemanticValue> {
|
|
145
|
+
return (semanticValue(value) || {}) as Record<string, SemanticValue>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function candidateSummary(value: unknown): MaterialOfferCandidateSummary | null {
|
|
149
|
+
const item = record(value);
|
|
150
|
+
if (!item) return null;
|
|
151
|
+
const captureUci = String(item.captureUci || "");
|
|
152
|
+
const offeredSquare = String(item.offeredSquare || "");
|
|
153
|
+
if (!/^[a-h][1-8][a-h][1-8][qrbn]?$/.test(captureUci) || !/^[a-h][1-8]$/.test(offeredSquare)) return null;
|
|
154
|
+
return {
|
|
155
|
+
captureUci,
|
|
156
|
+
offeredSquare,
|
|
157
|
+
offeredPiece: String(item.offeredPiece || "unknown").slice(0, 24),
|
|
158
|
+
nominalOffer: finiteNumber(item.nominalOffer),
|
|
159
|
+
voluntaryOfferCap: finiteNumber(item.voluntaryOfferCap),
|
|
160
|
+
bestImmediateRecoveryNet: finiteNumber(item.bestImmediateRecoveryNet),
|
|
161
|
+
qualifying: item.qualifying === true,
|
|
162
|
+
acceptanceLine: moveList(item.acceptanceLine),
|
|
163
|
+
persistent: typeof item.persistent === "boolean" ? item.persistent : null,
|
|
164
|
+
conversionGain: item.conversionGain == null ? null : finiteNumber(item.conversionGain),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function normalizeMaterialOfferSet(value: unknown): MaterialOfferSet | null {
|
|
169
|
+
const material = record(value);
|
|
170
|
+
if (!material || material.version !== MATERIAL_OFFER_SET_VERSION) return null;
|
|
171
|
+
const status = material.status === "complete" ? "complete" : "unavailable";
|
|
172
|
+
const screenedCandidates = Array.isArray(material.screenedCandidates)
|
|
173
|
+
? material.screenedCandidates.map(candidateSummary).filter((item): item is MaterialOfferCandidateSummary => Boolean(item))
|
|
174
|
+
: [];
|
|
175
|
+
const offers = status === "complete" && Array.isArray(material.offers)
|
|
176
|
+
? material.offers.map(candidateSummary).filter((item): item is MaterialOfferCandidateSummary => Boolean(item && item.qualifying))
|
|
177
|
+
: null;
|
|
178
|
+
if (status === "complete" && offers === null) return null;
|
|
179
|
+
const horizon = record(material.horizon) || {};
|
|
180
|
+
return {
|
|
181
|
+
version: MATERIAL_OFFER_SET_VERSION,
|
|
182
|
+
status,
|
|
183
|
+
offers,
|
|
184
|
+
screenedCandidates,
|
|
185
|
+
classifierVersion: Math.max(0, Math.trunc(finiteNumber(material.classifierVersion))),
|
|
186
|
+
detector: "classifier-sacrifice-offer-tracker",
|
|
187
|
+
policy: String(material.policy || "unknown").slice(0, 120),
|
|
188
|
+
horizon: {
|
|
189
|
+
verificationDepth: horizon.verificationDepth == null ? null : Math.max(0, Math.trunc(finiteNumber(horizon.verificationDepth))),
|
|
190
|
+
materialWindowPlies: horizon.materialWindowPlies == null ? null : Math.max(0, Math.trunc(finiteNumber(horizon.materialWindowPlies))),
|
|
191
|
+
principalVariationPlies: Math.max(0, Math.trunc(finiteNumber(horizon.principalVariationPlies))),
|
|
192
|
+
},
|
|
193
|
+
limitations: Array.isArray(material.limitations) ? material.limitations.map(String).slice(0, 8).map((item) => item.slice(0, 300)) : [],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function boundedCandidate(value: unknown, rootFen: string) {
|
|
198
|
+
const item = record(value);
|
|
199
|
+
if (!item) return null;
|
|
200
|
+
const uci = boundedText(item.uci || moveList(item.pv, 1)[0], 8);
|
|
201
|
+
if (!/^[a-h][1-8][a-h][1-8][qrbn]?$/.test(uci)) return null;
|
|
202
|
+
const source = record(item.scoreProvenance);
|
|
203
|
+
const reportedDepth = Math.max(0, Math.trunc(finiteNumber(item.depth)));
|
|
204
|
+
const originKind = boundedText(source?.kind || 'legacy-unknown', 40);
|
|
205
|
+
const originFen = typeof source?.fen === 'string' ? source.fen.slice(0, 120) : null;
|
|
206
|
+
const originatingDepth = source?.originatingDepth == null ? null : nullableNumber(source.originatingDepth);
|
|
207
|
+
const actualDepth = originKind === 'root-search' && originFen === rootFen && originatingDepth === reportedDepth && reportedDepth > 0
|
|
208
|
+
? originatingDepth : null;
|
|
209
|
+
return {
|
|
210
|
+
uci,
|
|
211
|
+
san: boundedText(item.san, 20),
|
|
212
|
+
scoreCp: nullableNumber(item.scoreCp),
|
|
213
|
+
mate: nullableNumber(item.mate),
|
|
214
|
+
reportedDepth,
|
|
215
|
+
actualDepth,
|
|
216
|
+
scoreOrigin: { kind: originKind, fen: originFen, originatingDepth },
|
|
217
|
+
quality: boundedText(item.quality, 24),
|
|
218
|
+
pv: moveList(item.pv, 10),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Canonical edge analysis stores bounded alternatives in the classifier's
|
|
224
|
+
* compact `alternative_candidates` shape. Retain its scores and legal lines
|
|
225
|
+
* with the reported depth and score origin, and expose mechanical root/line
|
|
226
|
+
* pointers used by a live `compare_moves` response. These are intentionally
|
|
227
|
+
* just replayable UCI pointers: scores and lines remain bounded engine facts,
|
|
228
|
+
* and neither pointer asserts why a root is preferable.
|
|
229
|
+
*/
|
|
230
|
+
function compactComparisonPointers(candidates: Array<NonNullable<ReturnType<typeof boundedCandidate>>>) {
|
|
231
|
+
const rootMoves = [...new Set(candidates.map((candidate) => candidate.uci))];
|
|
232
|
+
const continuationMoveFacts = candidates.flatMap((candidate) => {
|
|
233
|
+
const moves = candidate.pv
|
|
234
|
+
.filter((uci) => /^[a-h][1-8][a-h][1-8][qrbn]?$/.test(uci))
|
|
235
|
+
.map((uci) => ({ uci }));
|
|
236
|
+
return moves.length && moves[0].uci === candidate.uci
|
|
237
|
+
? [{ rootMove: candidate.uci, moves }]
|
|
238
|
+
: [];
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
rootMoves,
|
|
242
|
+
continuationMoveFacts,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function legalReplayLine(fen: string, line: string[]) {
|
|
247
|
+
const board = new Chess(fen);
|
|
248
|
+
for (const uci of line) {
|
|
249
|
+
const move = board.moves({ verbose: true }).find((candidate) => `${candidate.from}${candidate.to}${candidate.promotion || ""}` === uci);
|
|
250
|
+
if (!move) return false;
|
|
251
|
+
board.move(move);
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function analysisRecord(input: Omit<ExistingAnalysisRecord, "version" | "id" | "licenses"> & { idSeed: unknown; licenses?: EvidenceLicense[] }): ExistingAnalysisRecord {
|
|
257
|
+
const licenseInput = {
|
|
258
|
+
kind: "context",
|
|
259
|
+
source: input.provenance.provider,
|
|
260
|
+
authority: input.authority,
|
|
261
|
+
proofStrength: factProofStrength(input.authority, { exhaustive: input.coverage.complete, unresolved: input.coverage.unresolved }),
|
|
262
|
+
data: input.data,
|
|
263
|
+
coverage: input.coverage,
|
|
264
|
+
replay: input.replay,
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
version: EXISTING_ANALYSIS_RECORD_VERSION,
|
|
268
|
+
id: `analysis-context:${stableHash(input.idSeed)}`,
|
|
269
|
+
kind: input.kind,
|
|
270
|
+
scope: input.scope,
|
|
271
|
+
authority: input.authority,
|
|
272
|
+
licenses: validateEvidenceLicenseClaims(licenseInput, input.licenses || []),
|
|
273
|
+
summary: input.summary,
|
|
274
|
+
data: input.data,
|
|
275
|
+
coverage: input.coverage,
|
|
276
|
+
provenance: input.provenance,
|
|
277
|
+
replay: input.replay,
|
|
278
|
+
limitations: input.limitations,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function selectedEdgeRecords(value: unknown, context: ReasoningPositionContext): ExistingAnalysisRecord[] {
|
|
283
|
+
const snapshot = record(value);
|
|
284
|
+
const selected = record(snapshot?.selectedEdge);
|
|
285
|
+
const analysis = record(selected?.analysis);
|
|
286
|
+
if (!snapshot || snapshot.version !== CANONICAL_EDGE_ANALYSIS_SNAPSHOT_VERSION || !selected || !analysis) return [];
|
|
287
|
+
if (String(selected.moveUci || "") !== context.moveUci || String(selected.moveSan || "") !== context.moveSan) return [];
|
|
288
|
+
const classifierVersion = Math.max(0, Math.trunc(finiteNumber(analysis.classifier_version)));
|
|
289
|
+
const engine = boundedText(analysis.engine || "unknown", 80);
|
|
290
|
+
const result: ExistingAnalysisRecord[] = [];
|
|
291
|
+
const classification = boundedText(analysis.classification, 40);
|
|
292
|
+
if (classification) {
|
|
293
|
+
result.push(analysisRecord({
|
|
294
|
+
idSeed: { kind: "classification.label", context: context.nodeId, classifierVersion, classification },
|
|
295
|
+
kind: "classification.label",
|
|
296
|
+
scope: "selected-edge",
|
|
297
|
+
authority: "heuristic-affordance",
|
|
298
|
+
licenses: [EVIDENCE_LICENSE.ATTENTION_AFFORDANCE],
|
|
299
|
+
summary: `Knight's Eye classified ${context.moveSan} as ${classification} under classifier policy ${classifierVersion}. The label reports the product decision; it does not prove why the move deserves it.`,
|
|
300
|
+
data: { label: classification, classifierVersion, policy: `move-classification-policy/${classifierVersion}`, authoritativeScope: "reported-label-only" },
|
|
301
|
+
coverage: { complete: true, covered: ["reported-label"], unresolved: ["semantic-explanation"] },
|
|
302
|
+
provenance: { tier: ANALYSIS_CONTEXT_PROVENANCE_TIER.POLICY_LABEL, provider: "knights-eye/move-classifier", version: String(classifierVersion), derivedFromIds: [] },
|
|
303
|
+
replay: { moveUci: context.moveUci },
|
|
304
|
+
limitations: ["A user-facing classification label is not independent evidence for its chess explanation."],
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const legalRoots = new Set(new Chess(context.fenBefore).moves({ verbose: true }).map((move) => `${move.from}${move.to}${move.promotion || ""}`));
|
|
309
|
+
const alternatives = Array.isArray(analysis.alternative_candidates)
|
|
310
|
+
? analysis.alternative_candidates.map((candidate:unknown) => boundedCandidate(candidate,context.fenBefore)).filter((item): item is NonNullable<ReturnType<typeof boundedCandidate>> => Boolean(item)).slice(0, 5)
|
|
311
|
+
.filter((item) => legalRoots.has(item.uci) && (!item.pv.length || item.pv[0] === item.uci) && legalReplayLine(context.fenBefore, item.pv))
|
|
312
|
+
: [];
|
|
313
|
+
const rawBestMove = boundedText(analysis.bestmove, 8);
|
|
314
|
+
const bestMove = legalRoots.has(rawBestMove) ? rawBestMove : "";
|
|
315
|
+
if (analysis.eval_before != null || analysis.eval_after != null || analysis.mate_before != null || analysis.mate_after != null || bestMove || alternatives.length) {
|
|
316
|
+
const lines = alternatives.map((item) => item.pv).filter((line) => line.length);
|
|
317
|
+
const comparisonPointers = compactComparisonPointers(alternatives);
|
|
318
|
+
const engineData: Record<string, SemanticValue> = {
|
|
319
|
+
engine,
|
|
320
|
+
reportedDepth: Math.max(0, Math.trunc(finiteNumber(analysis.depth || analysis.processing_depth))),
|
|
321
|
+
requestedDepth: Math.max(0, Math.trunc(finiteNumber(analysis.requested_depth))),
|
|
322
|
+
scorePerspective: "selected-mover",
|
|
323
|
+
evaluationBeforeCp: nullableNumber(analysis.eval_before),
|
|
324
|
+
evaluationAfterCpForMover: nullableNumber(analysis.eval_after),
|
|
325
|
+
centipawnLoss: nullableNumber(analysis.cp_loss),
|
|
326
|
+
mateBefore: nullableNumber(analysis.mate_before),
|
|
327
|
+
mateAfterForMover: nullableNumber(analysis.mate_after),
|
|
328
|
+
bestMoveUci: bestMove,
|
|
329
|
+
bestMoveSan: boundedText(analysis.bestmove_san, 20),
|
|
330
|
+
alternatives: alternatives as unknown as SemanticValue,
|
|
331
|
+
// Normalized mechanical pointers let semantic planning/criticism apply
|
|
332
|
+
// the same bounded-comparison coverage rules to canonical analysis and
|
|
333
|
+
// to a live compare_moves result. They deliberately carry no inferred
|
|
334
|
+
// purpose, forcedness, or preference beyond the stored engine record.
|
|
335
|
+
rootMoves: comparisonPointers.rootMoves as unknown as SemanticValue,
|
|
336
|
+
continuationMoveFacts: comparisonPointers.continuationMoveFacts as unknown as SemanticValue,
|
|
337
|
+
};
|
|
338
|
+
result.push(analysisRecord({
|
|
339
|
+
idSeed: { kind: "engine.bounded-assessment", context: context.nodeId, engineData },
|
|
340
|
+
kind: "engine.bounded-assessment",
|
|
341
|
+
scope: "selected-edge",
|
|
342
|
+
authority: "bounded-engine",
|
|
343
|
+
licenses: [EVIDENCE_LICENSE.ENGINE_BOUNDED_SCORE, ...(alternatives.length ? [EVIDENCE_LICENSE.ENGINE_BOUNDED_COMPARISON] : [])],
|
|
344
|
+
summary: `Canonical ${engine} analysis records the bounded evaluation around ${context.moveSan}${bestMove ? ` and the best move ${boundedText(analysis.bestmove_san || bestMove, 20)}` : ""}.`,
|
|
345
|
+
data: engineData,
|
|
346
|
+
coverage: { complete: false, covered: alternatives.map((item) => item.uci), unresolved: ["bounded-search", "human-purpose"] },
|
|
347
|
+
provenance: { tier: ANALYSIS_CONTEXT_PROVENANCE_TIER.RAW_ANALYSIS, provider: `knights-eye/${engine}/compare-moves`, version: boundedText(analysis.tree_analysis_version || "unknown", 40), derivedFromIds: [] },
|
|
348
|
+
// Use the shared evidence replay key so every canonical bounded-engine
|
|
349
|
+
// PV is eligible for a typed move reference only after replaying from
|
|
350
|
+
// this exact request position. This is identity/legality transport,
|
|
351
|
+
// not an expansion of the engine record's semantic authority.
|
|
352
|
+
replay: { fen: context.fenBefore, fenBefore: context.fenBefore, targetMoveUci: context.moveUci, linesUci: lines as unknown as SemanticValue, depth: Math.max(0, Math.trunc(finiteNumber(analysis.depth || analysis.processing_depth))), threads: 1 },
|
|
353
|
+
limitations: ["A bounded engine comparison establishes only the recorded scores and continuations, not human intent, uniqueness beyond its coverage, or a semantic plan."],
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const materialExchange = record(analysis.material_exchange);
|
|
358
|
+
if (materialExchange) {
|
|
359
|
+
result.push(analysisRecord({
|
|
360
|
+
idSeed: { kind: "semantic.lifecycle.material-exchange", context: context.nodeId, materialExchange },
|
|
361
|
+
kind: "semantic.lifecycle.material-exchange",
|
|
362
|
+
scope: "selected-edge",
|
|
363
|
+
authority: "heuristic-affordance",
|
|
364
|
+
licenses: [EVIDENCE_LICENSE.ATTENTION_AFFORDANCE],
|
|
365
|
+
summary: `The classifier's attributed material-exchange tracker recorded transition ${boundedText(materialExchange.transition || "unknown", 40)} at ${context.moveSan}.`,
|
|
366
|
+
data: semanticRecord(materialExchange),
|
|
367
|
+
coverage: { complete: false, covered: ["tracked-material-thread"], unresolved: ["semantic-purpose", "compensation"] },
|
|
368
|
+
provenance: { tier: ANALYSIS_CONTEXT_PROVENANCE_TIER.LEGACY_HEURISTIC, provider: "knights-eye/material-exchange-tracker", version: String(classifierVersion), derivedFromIds: [] },
|
|
369
|
+
replay: { moveUci: context.moveUci, fenBefore: context.fenBefore },
|
|
370
|
+
limitations: ["Lifecycle names are attributed reusable context, not proof that the move has a particular purpose or compensation."],
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const missed = record(analysis.missed_opportunity);
|
|
375
|
+
if (missed) {
|
|
376
|
+
result.push(analysisRecord({
|
|
377
|
+
idSeed: { kind: "classifier.missed-opportunity", context: context.nodeId, missed },
|
|
378
|
+
kind: "classifier.missed-opportunity",
|
|
379
|
+
scope: "selected-edge",
|
|
380
|
+
authority: "heuristic-affordance",
|
|
381
|
+
licenses: [EVIDENCE_LICENSE.ATTENTION_AFFORDANCE],
|
|
382
|
+
summary: `The classifier emitted an attributed missed-opportunity diagnostic for ${context.moveSan}; its underlying fields may guide inspection but do not prove the diagnosis.`,
|
|
383
|
+
data: semanticRecord(missed),
|
|
384
|
+
coverage: { complete: false, covered: ["classifier-diagnostic"], unresolved: ["semantic-explanation"] },
|
|
385
|
+
provenance: { tier: ANALYSIS_CONTEXT_PROVENANCE_TIER.LEGACY_HEURISTIC, provider: "knights-eye/missed-opportunity-diagnostic", version: String(classifierVersion), derivedFromIds: [] },
|
|
386
|
+
replay: { moveUci: context.moveUci, fenBefore: context.fenBefore },
|
|
387
|
+
limitations: ["This diagnostic is an affordance. Its asserted chess meaning requires independent licensed evidence."],
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function pathRecords(value: unknown, context: ReasoningPositionContext): ExistingAnalysisRecord[] {
|
|
394
|
+
const snapshot = record(value);
|
|
395
|
+
const items = Array.isArray(snapshot?.pathEdges) ? snapshot.pathEdges : [];
|
|
396
|
+
return items.slice(-4).flatMap((raw) => {
|
|
397
|
+
const edge = record(raw);
|
|
398
|
+
const analysis = record(edge?.analysis);
|
|
399
|
+
const lifecycle = record(analysis?.material_exchange);
|
|
400
|
+
const moveUci = boundedText(edge?.moveUci, 8);
|
|
401
|
+
if (!edge || !analysis || !lifecycle || !context.historyUci.includes(moveUci)) return [];
|
|
402
|
+
const classifierVersion = Math.max(0, Math.trunc(finiteNumber(analysis.classifier_version)));
|
|
403
|
+
return [analysisRecord({
|
|
404
|
+
idSeed: { kind: "path.semantic.lifecycle.material-exchange", edgeId: edge.edgeId, lifecycle },
|
|
405
|
+
kind: "path.semantic.lifecycle.material-exchange",
|
|
406
|
+
scope: "path-window",
|
|
407
|
+
authority: "heuristic-affordance",
|
|
408
|
+
licenses: [EVIDENCE_LICENSE.ATTENTION_AFFORDANCE],
|
|
409
|
+
summary: `A prior path edge ${boundedText(edge.moveSan || moveUci, 20)} carries an attributed material-exchange lifecycle record.`,
|
|
410
|
+
data: { edgeId: boundedText(edge.edgeId, 120), moveUci, moveSan: boundedText(edge.moveSan, 20), lifecycle: semanticRecord(lifecycle) },
|
|
411
|
+
coverage: { complete: false, covered: ["bounded-prior-path-record"], unresolved: ["path-relevance", "semantic-purpose"] },
|
|
412
|
+
provenance: { tier: ANALYSIS_CONTEXT_PROVENANCE_TIER.LEGACY_HEURISTIC, provider: "knights-eye/material-exchange-tracker", version: String(classifierVersion), derivedFromIds: [] },
|
|
413
|
+
replay: { historyStartFen: context.historyStartFen || "", historyUci: context.historyUci as unknown as SemanticValue, moveUci },
|
|
414
|
+
limitations: ["Path-scoped lifecycle context is included only as an affordance; the model must establish its relevance independently."],
|
|
415
|
+
})];
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function wantsPathContext(question: string) {
|
|
420
|
+
return /\b(earlier|later|previous|prior|subsequent|persist(?:s|ed|ent)?|throughout|over the game|offer|sacrific|conversion|develop(?:s|ed|ment)?)\b/i.test(question);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function normalizeExistingAnalysisContext(value: unknown, context: ReasoningPositionContext, options: { question?: string; maxRecords?: number } = {}): ExistingAnalysisContext | null {
|
|
424
|
+
const input = record(value);
|
|
425
|
+
if (!input || (input.version !== EXISTING_ANALYSIS_CONTEXT_VERSION && input.version !== LEGACY_EXISTING_ANALYSIS_CONTEXT_VERSION)) return null;
|
|
426
|
+
const materialOffers = normalizeMaterialOfferSet(input.materialOffers);
|
|
427
|
+
if (!materialOffers) return null;
|
|
428
|
+
const after = new Chess(context.fenAfter);
|
|
429
|
+
for (const candidate of [...materialOffers.screenedCandidates, ...(materialOffers.offers || [])]) {
|
|
430
|
+
if (!after.moves({ verbose: true }).some((move) => `${move.from}${move.to}${move.promotion || ""}` === candidate.captureUci)) return null;
|
|
431
|
+
}
|
|
432
|
+
const allRecords = input.version === EXISTING_ANALYSIS_CONTEXT_VERSION ? [...selectedEdgeRecords(input.canonicalAnalysis, context), ...pathRecords(input.canonicalAnalysis, context)] : [];
|
|
433
|
+
const relevant = allRecords.filter((item) => item.scope === "selected-edge" || wantsPathContext(options.question || ""));
|
|
434
|
+
const omitted: ExistingAnalysisContext["coverage"]["omitted"] = allRecords
|
|
435
|
+
.filter((item) => !relevant.includes(item))
|
|
436
|
+
.map((item) => ({ kind: item.kind, reason: "relevance" }));
|
|
437
|
+
const maximum = Math.max(1, Math.min(16, Math.trunc(options.maxRecords || 8)));
|
|
438
|
+
const records = relevant.slice(0, maximum);
|
|
439
|
+
omitted.push(...relevant.slice(maximum).map((item) => ({ kind: item.kind, reason: "budget" as const })));
|
|
440
|
+
const unavailableKinds = ["classification.policy-definition", "classifier.uniqueness-and-criticality", "semantic.reviewed-idea-graph"];
|
|
441
|
+
if (!record(input.canonicalAnalysis)) unavailableKinds.unshift("canonical.selected-edge-analysis");
|
|
442
|
+
return {
|
|
443
|
+
version: EXISTING_ANALYSIS_CONTEXT_VERSION,
|
|
444
|
+
materialOffers,
|
|
445
|
+
records,
|
|
446
|
+
coverage: { complete: omitted.length === 0 && unavailableKinds.length === 0, includedKinds: [...new Set(records.map((item) => item.kind))], omitted, unavailableKinds },
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function materialOfferObservation(context: ReasoningPositionContext, material: MaterialOfferSet, contextHash: string, timeline: ReturnType<typeof createPocIdentityTimeline>) {
|
|
451
|
+
const moveParticipant = participant(PARTICIPANT_ROLES.TARGET, REFERENCE_DOMAINS.MOVE, context.moveUci);
|
|
452
|
+
const offerParticipants = material.screenedCandidates.flatMap((candidate) => {
|
|
453
|
+
const pieceId = pieceIdAt(timeline.after, candidate.offeredSquare as Square);
|
|
454
|
+
return pieceId ? [participant(PARTICIPANT_ROLES.SUBJECT, REFERENCE_DOMAINS.PIECE, pieceId)] : [];
|
|
455
|
+
});
|
|
456
|
+
const identity = { provider: EXISTING_ANALYSIS_OBSERVATION_PROVIDER_ID, version: EXISTING_ANALYSIS_CONTEXT_VERSION, classifierVersion: material.classifierVersion, detector: material.detector, policy: material.policy, moveUci: context.moveUci };
|
|
457
|
+
const evidenceId = `evidence:analysis-context:${stableHash({ contextHash, identity, material })}`;
|
|
458
|
+
const observationId = `observation:${stableHash({ contextHash, identity })}`;
|
|
459
|
+
const offerCount = material.offers?.length || 0;
|
|
460
|
+
const summary = offerCount ? `Knight's Eye classifier offer tracking found ${offerCount} qualifying material offer${offerCount === 1 ? "" : "s"} for ${context.moveSan} within its declared policy and horizon.` : `Knight's Eye classifier offer tracking completed for ${context.moveSan} and found no qualifying material offers within its declared policy and horizon.`;
|
|
461
|
+
const coverage = { complete: true, covered: (material.offers || []).map((offer) => `${offer.captureUci}:${offer.offeredSquare}`), unresolved: [] as string[] };
|
|
462
|
+
const data = { observationId, observationType: "analysis-context:material-offer-set", authority: "heuristic-affordance", offers: (material.offers || []) as unknown as SemanticValue, screenedCandidates: material.screenedCandidates as unknown as SemanticValue, classifierVersion: material.classifierVersion, detector: material.detector, policy: material.policy, horizon: material.horizon as unknown as SemanticValue };
|
|
463
|
+
const evidence: ReasoningEvidenceRecord = withEvidenceLicenses({ schemaVersion: REASONING_POC_VERSION, id: evidenceId, contextHash, kind: "context", source: `${EXISTING_ANALYSIS_OBSERVATION_PROVIDER_ID}/${EXISTING_ANALYSIS_CONTEXT_VERSION}/classifier-${material.classifierVersion}`, proofStrength: null, summary: `${summary} This attributed classifier result is an observation, not independent proof of chess meaning.`, data, coverage, replay: { moveUci: context.moveUci, detector: material.detector, classifierVersion: material.classifierVersion, policy: material.policy }, licenses: [EVIDENCE_LICENSE.ATTENTION_AFFORDANCE] });
|
|
464
|
+
const observation: ReasoningObservation = { schemaVersion: REASONING_POC_VERSION, id: observationId, contextHash, observationType: "analysis-context:material-offer-set", participants: [moveParticipant, ...offerParticipants], attributes: { authority: "heuristic-affordance", offers: (material.offers || []) as unknown as SemanticValue, screenedCandidates: material.screenedCandidates as unknown as SemanticValue, coverageComplete: true, detector: material.detector, policy: material.policy, classifierVersion: material.classifierVersion, horizon: material.horizon as unknown as SemanticValue }, exactness: "heuristic", provenance: { provider: EXISTING_ANALYSIS_OBSERVATION_PROVIDER_ID, version: EXISTING_ANALYSIS_CONTEXT_VERSION, trust: "deterministic", methods: [material.detector, material.policy] }, factEvidenceIds: [evidenceId], probeRecipes: [] };
|
|
465
|
+
return { evidence, observation };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export class ExistingAnalysisObservationProvider implements ObservationProvider {
|
|
469
|
+
readonly id = EXISTING_ANALYSIS_OBSERVATION_PROVIDER_ID;
|
|
470
|
+
readonly version = EXISTING_ANALYSIS_CONTEXT_VERSION;
|
|
471
|
+
|
|
472
|
+
constructor(
|
|
473
|
+
private readonly existing: ExistingAnalysisContext | null,
|
|
474
|
+
private readonly mode: ExistingAnalysisContextMode = EXISTING_ANALYSIS_CONTEXT_MODE.FULL,
|
|
475
|
+
) {}
|
|
476
|
+
|
|
477
|
+
observe(context: ReasoningPositionContext, _controls: ReasoningRunControls, signal?: AbortSignal): ReasoningObservationSet {
|
|
478
|
+
if (signal?.aborted) throw signal.reason || new Error("Existing-analysis observation extraction cancelled");
|
|
479
|
+
const contextHash = reasoningContextHash(context);
|
|
480
|
+
const existing = this.existing;
|
|
481
|
+
if (!existing || this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.HIDDEN) {
|
|
482
|
+
return {
|
|
483
|
+
schemaVersion: REASONING_POC_VERSION,
|
|
484
|
+
contextHash,
|
|
485
|
+
observations: [],
|
|
486
|
+
evidence: [],
|
|
487
|
+
providers: [{ provider: this.id, version: this.version }],
|
|
488
|
+
limitations: [this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.HIDDEN
|
|
489
|
+
? "Existing analysis context was deliberately hidden by the development ablation control."
|
|
490
|
+
: "No valid existing analysis context was available for this selected edge."],
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const timeline = createPocIdentityTimeline(context);
|
|
494
|
+
const includeLabels = this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.LABEL_ONLY || this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.FULL;
|
|
495
|
+
const includeDiagnostics = this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.DIAGNOSTICS_ONLY || this.mode === EXISTING_ANALYSIS_CONTEXT_MODE.FULL;
|
|
496
|
+
const materialAvailable = includeDiagnostics && existing.materialOffers.status === "complete" && Array.isArray(existing.materialOffers.offers);
|
|
497
|
+
const observations: ReasoningObservation[] = [];
|
|
498
|
+
const evidence: ReasoningEvidenceRecord[] = [];
|
|
499
|
+
if (materialAvailable) {
|
|
500
|
+
const material = materialOfferObservation(context, existing.materialOffers, contextHash, timeline);
|
|
501
|
+
observations.push(material.observation);
|
|
502
|
+
evidence.push(material.evidence);
|
|
503
|
+
}
|
|
504
|
+
const selectedRecords = existing.records.filter((item) => item.kind === "classification.label" ? includeLabels : includeDiagnostics);
|
|
505
|
+
const providers = [
|
|
506
|
+
{ provider: this.id, version: this.version },
|
|
507
|
+
...Array.from(new Map(selectedRecords.map((item) => [
|
|
508
|
+
`${item.provenance.provider}\u0000${item.provenance.version}`,
|
|
509
|
+
{ provider: item.provenance.provider, version: item.provenance.version },
|
|
510
|
+
])).values()),
|
|
511
|
+
];
|
|
512
|
+
for (const item of selectedRecords) {
|
|
513
|
+
if (!isChessFactAuthority(item.authority)) continue;
|
|
514
|
+
const observationId = `observation:${stableHash({ contextHash, analysisRecordId: item.id })}`;
|
|
515
|
+
const evidenceId = `evidence:${stableHash({ contextHash, analysisRecordId: item.id })}`;
|
|
516
|
+
const pathMoveUci = item.scope === "path-window" ? String(item.data.moveUci || "") : "";
|
|
517
|
+
const participants = pathMoveUci && context.historyUci.includes(pathMoveUci)
|
|
518
|
+
? [
|
|
519
|
+
participant(PARTICIPANT_ROLES.SUBJECT, REFERENCE_DOMAINS.MOVE, pathMoveUci),
|
|
520
|
+
participant(PARTICIPANT_ROLES.TARGET, REFERENCE_DOMAINS.MOVE, context.moveUci),
|
|
521
|
+
]
|
|
522
|
+
: [participant(PARTICIPANT_ROLES.TARGET, REFERENCE_DOMAINS.MOVE, context.moveUci)];
|
|
523
|
+
evidence.push(withEvidenceLicenses({ schemaVersion: REASONING_POC_VERSION, id: evidenceId, contextHash, kind: "context", source: `${item.provenance.provider}/${item.provenance.version}`, proofStrength: factProofStrength(item.authority, { exhaustive: item.coverage.complete, unresolved: item.coverage.unresolved }), summary: item.summary, data: { observationId, observationType: `analysis-context:${item.kind}`, authority: item.authority, provenanceTier: item.provenance.tier, analysisRecordId: item.id, analysis: item.data }, coverage: item.coverage, replay: item.replay, licenses: item.licenses }));
|
|
524
|
+
observations.push({ schemaVersion: REASONING_POC_VERSION, id: observationId, contextHash, observationType: `analysis-context:${item.kind}`, participants, attributes: { authority: item.authority, provenanceTier: item.provenance.tier, analysisRecordId: item.id, data: item.data, coverage: item.coverage as unknown as SemanticValue }, exactness: item.authority === "legal-exact" || item.authority === "deterministic-derived" ? "exact" : "heuristic", provenance: { provider: item.provenance.provider, version: item.provenance.version, trust: item.provenance.tier === ANALYSIS_CONTEXT_PROVENANCE_TIER.RAW_ANALYSIS ? "external" : "deterministic", methods: [item.kind, item.provenance.tier] }, factEvidenceIds: [evidenceId], probeRecipes: [] });
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
schemaVersion: REASONING_POC_VERSION,
|
|
528
|
+
contextHash,
|
|
529
|
+
observations,
|
|
530
|
+
evidence,
|
|
531
|
+
providers,
|
|
532
|
+
limitations: [
|
|
533
|
+
...(materialAvailable ? existing.materialOffers.limitations : []),
|
|
534
|
+
...(materialAvailable ? ["An empty offers array means this named tracker completed its declared policy and horizon and found none; it is not proof that no conceivable sacrifice exists."] : []),
|
|
535
|
+
`Existing analysis context ablation mode: ${this.mode}. Omitted context families were deliberately withheld from this development run.`,
|
|
536
|
+
"Existing analysis context may affect attention, hypothesis formation, comparison, and tool choice. It does not become chess truth merely because Knight's Eye stored it.",
|
|
537
|
+
"The final classification label is authoritative only about what Knight's Eye called the move; it is not proof of the explanation behind that label.",
|
|
538
|
+
"Anti-circularity: a policy label derived from semantic state cannot be reused as independent evidence for reconstructing or validating that same state.",
|
|
539
|
+
...selectedRecords.flatMap((item) => item.limitations),
|
|
540
|
+
...existing.coverage.unavailableKinds.map((kind) => `Existing analysis context kind ${kind} is unavailable in this adapter version.`),
|
|
541
|
+
],
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|