onto-mcp 0.4.12 → 0.4.13
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/.onto/authority/core-lexicon.yaml +1 -1
- package/.onto/authority/supported-models.yaml +137 -0
- package/.onto/domains/accounting-kr/competency_qs.md +153 -153
- package/.onto/processes/reconstruct/obligation-coverage-ledger.yaml +842 -0
- package/.onto/processes/reconstruct/obligation-coverage-recorded.yaml +225 -0
- package/.onto/processes/reconstruct/ontology-seeding-and-maturation-design.md +93 -54
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +169 -8
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +72 -1
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +35 -0
- package/.onto/processes/review/productized-live-path.md +9 -5
- package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
- package/.onto/processes/review/review-target-profile-contract.md +47 -4
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
- package/.onto/processes/shared/target-material-kind-contract.md +1 -1
- package/README.md +64 -1
- package/dist/cli.js +11 -1
- package/dist/core-api/reconstruct-api.js +575 -24
- package/dist/core-api/review-api.js +70 -5
- package/dist/core-api/review-progress.js +7 -0
- package/dist/core-api/runtime-observation.js +7 -0
- package/dist/core-runtime/artifact-io.js +75 -3
- package/dist/core-runtime/cli/claude-code-review-unit-executor.js +19 -8
- package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
- package/dist/core-runtime/cli/prepare-review-session.js +34 -1
- package/dist/core-runtime/cli/render-review-final-output.js +2 -1
- package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
- package/dist/core-runtime/cli/run-seats.js +18 -0
- package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
- package/dist/core-runtime/cli/unit-resubmit.js +269 -0
- package/dist/core-runtime/cli/worker-structured-output.js +80 -2
- package/dist/core-runtime/discovery/host-detection.js +71 -22
- package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
- package/dist/core-runtime/discovery/review-cert-record.js +442 -0
- package/dist/core-runtime/discovery/review-execution-units.js +22 -0
- package/dist/core-runtime/discovery/seat-inventory.js +167 -0
- package/dist/core-runtime/discovery/settings-chain.js +311 -52
- package/dist/core-runtime/discovery/supported-models.js +282 -29
- package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
- package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
- package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
- package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
- package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
- package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
- package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
- package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
- package/dist/core-runtime/effort-calibration-ingest.js +135 -0
- package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
- package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
- package/dist/core-runtime/llm/llm-caller.js +188 -47
- package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
- package/dist/core-runtime/llm/model-switcher.js +1 -1
- package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
- package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
- package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
- package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
- package/dist/core-runtime/onboard/cli-host.js +5 -2
- package/dist/core-runtime/onboard/configure-provider.js +33 -1
- package/dist/core-runtime/onboard/host-target.js +28 -3
- package/dist/core-runtime/onboard/register.js +54 -12
- package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +35 -0
- package/dist/core-runtime/reconstruct/claim-projection-validation.js +17 -0
- package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
- package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
- package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
- package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
- package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
- package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
- package/dist/core-runtime/reconstruct/execution-telemetry.js +83 -9
- package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
- package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
- package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
- package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
- package/dist/core-runtime/reconstruct/material-admission-validation.js +18 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +14 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +304 -18
- package/dist/core-runtime/reconstruct/maturation-validation.js +1231 -53
- package/dist/core-runtime/reconstruct/mock-llm-realization.js +107 -3
- package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
- package/dist/core-runtime/reconstruct/ontology-seed-validation.js +27 -0
- package/dist/core-runtime/reconstruct/output-budget.js +6 -0
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +87 -3
- package/dist/core-runtime/reconstruct/post-seed-validation.js +118 -5
- package/dist/core-runtime/reconstruct/purpose-authority-validation.js +117 -2
- package/dist/core-runtime/reconstruct/record.js +42 -0
- package/dist/core-runtime/reconstruct/registry-verification-validation.js +16 -0
- package/dist/core-runtime/reconstruct/run-control-validation.js +1011 -34
- package/dist/core-runtime/reconstruct/run.js +5198 -536
- package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +54 -3
- package/dist/core-runtime/reconstruct/semantic-quality-gate.js +105 -2
- package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +41 -0
- package/dist/core-runtime/reconstruct/source-observations.js +166 -0
- package/dist/core-runtime/reconstruct/source-safety-validation.js +71 -183
- package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +37 -1
- package/dist/core-runtime/reconstruct/terminal-validation.js +132 -2
- package/dist/core-runtime/review/continuation-plan.js +12 -5
- package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
- package/dist/core-runtime/review/materializers.js +100 -11
- package/dist/core-runtime/review/nesting-batch.js +6 -2
- package/dist/core-runtime/review/obligation-shardability.js +138 -0
- package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
- package/dist/core-runtime/review/review-artifact-utils.js +217 -5
- package/dist/core-runtime/review/review-execution-steps.js +32 -8
- package/dist/core-runtime/review/review-progress-contract.js +8 -0
- package/dist/core-runtime/review/review-prompt-budget.js +97 -0
- package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
- package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
- package/dist/core-runtime/route-identity.js +182 -0
- package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
- package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
- package/dist/core-runtime/target-material-kind.js +58 -0
- package/dist/mcp/server.js +12 -5
- package/dist/tui/app.js +167 -0
- package/dist/tui/data/event-follower.js +116 -0
- package/dist/tui/data/node-detail.js +42 -0
- package/dist/tui/data/projection-poll.js +26 -0
- package/dist/tui/data/session-discovery.js +65 -0
- package/dist/tui/index.js +108 -0
- package/dist/tui/screens/log.js +25 -0
- package/dist/tui/screens/node-detail.js +17 -0
- package/dist/tui/screens/session-selector.js +26 -0
- package/dist/tui/screens/workflow-tree.js +90 -0
- package/dist/tui/view-model/reconstruct-adapter.js +162 -0
- package/dist/tui/view-model/review-adapter.js +270 -0
- package/dist/tui/view-model/tree-view-model.js +19 -0
- package/package.json +18 -3
- package/settings.example.json +77 -16
|
@@ -3,7 +3,9 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { atomicWriteYamlDocument as writeYamlDocument } from "../artifact-io.js";
|
|
5
5
|
import { aggregateTargetMaterialDetections, detectTargetMaterialRefs, } from "../target-material-kind.js";
|
|
6
|
+
import { observeSpreadsheetSource, projectInventoryForAdmission, SPREADSHEET_CAPTURE_TRUNCATED_PHRASE, SPREADSHEET_MACRO_PRESENT_PHRASE, SPREADSHEET_OBSERVER_ADAPTER_ID, } from "../spreadsheet-structure-observer.js";
|
|
6
7
|
import { validateSourceObservationBoundary, } from "./source-observations.js";
|
|
8
|
+
import { buildDeterministicComprehensionArtifact } from "./comprehension-artifact.js";
|
|
7
9
|
import { loadReconstructSourceProfiles, } from "./source-profiles.js";
|
|
8
10
|
const CONCRETE_TARGET_MATERIAL_KINDS = new Set([
|
|
9
11
|
"code",
|
|
@@ -111,28 +113,173 @@ function defaultProfileForKind(profiles, kind) {
|
|
|
111
113
|
matchingProfiles[0];
|
|
112
114
|
}
|
|
113
115
|
/**
|
|
114
|
-
* Structural `content_excerpt` capture budgets (chars). Code and
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
116
|
+
* Structural `content_excerpt` capture budgets (chars). Code source and text-readable
|
|
117
|
+
* document prose are captured whole so the file tail (later definitions, document
|
|
118
|
+
* goals/milestones/decisions) reaches seed authoring instead of being lost to a
|
|
119
|
+
* leading slice — both are projected whole at the seed stage, so a small capture
|
|
120
|
+
* would silently author the seed from a partial file. Other kinds (binary documents,
|
|
121
|
+
* spreadsheet/database structural inventories) keep a small leading sample — the
|
|
122
|
+
* observation is a structural sample there, not the whole file.
|
|
123
|
+
*
|
|
124
|
+
* Capture is MODEL-AGNOSTIC: it runs before the seed-stage (provider, model) is
|
|
125
|
+
* resolved, so it captures up to a fixed `DOCUMENT_CAPTURE_CEILING_CHARS` ceiling
|
|
126
|
+
* (a disk/pathological-input safety bound, NOT a window bound). The seed-stage
|
|
127
|
+
* prompt PROJECTION then slices that captured prose to a dynamic, model-aware
|
|
128
|
+
* budget (see deriveDocumentExcerptProjectionBudget + run.ts). The ceiling is set
|
|
129
|
+
* comfortably above any projection budget so projection only ever narrows, never
|
|
130
|
+
* needs more than was captured. A document longer than the ceiling is captured-
|
|
131
|
+
* truncated (`excerpt_truncated`); one longer than the projection budget is
|
|
132
|
+
* projection-truncated at the seed stage (recorded durably there). True
|
|
133
|
+
* window-overflow recovery (selecting the relevant tail) is a later stage (see
|
|
134
|
+
* development-records/design/20260616-large-input-observation).
|
|
123
135
|
*
|
|
124
136
|
* Material kind is detected by extension (target-material-kind.ts `DOCUMENT_EXTENSIONS`),
|
|
125
137
|
* and `document` includes binary formats (.pdf/.docx/.ppt/.rtf) that `textStats` still
|
|
126
138
|
* reads as UTF-8. Only text-readable document formats earn the whole-document budget —
|
|
127
|
-
* a binary document keeps the small sample so we never capture
|
|
128
|
-
* bytes and spend the prompt on garbage; binary documents need an extraction step
|
|
129
|
-
* reconstruct. This set must be the text-prose subset of `DOCUMENT_EXTENSIONS`: an
|
|
139
|
+
* a binary document keeps the small sample so we never capture the ceiling in decoded
|
|
140
|
+
* binary bytes and spend the prompt on garbage; binary documents need an extraction step
|
|
141
|
+
* before reconstruct. This set must be the text-prose subset of `DOCUMENT_EXTENSIONS`: an
|
|
130
142
|
* extension that the classifier does not map to `document` (e.g. `.html`) would never
|
|
131
143
|
* reach this budget, so listing it here would be dead and misleading.
|
|
132
144
|
*/
|
|
133
145
|
const DEFAULT_STRUCTURAL_EXCERPT_CHAR_LIMIT = 6000;
|
|
134
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Capture ceiling (chars) for whole-document prose. Static and model-agnostic —
|
|
148
|
+
* the upper bound for both capture AND the dynamic projection clamp, so the
|
|
149
|
+
* captured excerpt is always a superset of any projection. Set well above the
|
|
150
|
+
* largest projection budget (opus/gpt-5.5 ~1M-token windows derive ~0.5M-char
|
|
151
|
+
* budgets) to leave headroom; the absolute bound is disk, not a model window.
|
|
152
|
+
*/
|
|
153
|
+
export const DOCUMENT_CAPTURE_CEILING_CHARS = 5_000_000;
|
|
154
|
+
/**
|
|
155
|
+
* Floor (chars) for the seed-stage document projection budget. The static value
|
|
156
|
+
* used when the model window is unknown (mock realization, provider-only seat, an
|
|
157
|
+
* unregistered model, or a registry entry without context_window_tokens). Equal to
|
|
158
|
+
* the prior fixed document excerpt budget, so a model-unaware run is unchanged.
|
|
159
|
+
*/
|
|
160
|
+
export const DOCUMENT_EXCERPT_PROJECTION_FLOOR = 200_000;
|
|
161
|
+
/**
|
|
162
|
+
* Fraction of the model context window the projection budget may consume, leaving
|
|
163
|
+
* margin for prompt instructions, non-excerpt payload, output tokens, and
|
|
164
|
+
* tokenization variance. Window-proportional (not a fixed char margin) so the
|
|
165
|
+
* margin scales with the window (C1).
|
|
166
|
+
*/
|
|
167
|
+
const WINDOW_BUDGET_FRACTION = 0.5;
|
|
168
|
+
/**
|
|
169
|
+
* Conservative LOWER bound on chars-per-token used to convert a token window into
|
|
170
|
+
* a char budget. NOT the average (~4 for English): CJK-dense prose tokenizes to
|
|
171
|
+
* far fewer chars per token (≈1 or below), so a low bound keeps the char budget
|
|
172
|
+
* from implying more tokens than the window holds (C1). This is an approximation
|
|
173
|
+
* pending a real tokenizer; the CJK-dense live fixture (P6) validates/calibrates
|
|
174
|
+
* it, and the re-design trigger lowers it if even this conservative budget
|
|
175
|
+
* overflows.
|
|
176
|
+
*/
|
|
177
|
+
const CHARS_PER_TOKEN_LB = 1;
|
|
178
|
+
/**
|
|
179
|
+
* Chars reserved from the derived budget for the prompt's instructions, the
|
|
180
|
+
* non-excerpt structural payload, and the model's output allowance — all of which
|
|
181
|
+
* also consume the window alongside the document excerpt (C1).
|
|
182
|
+
*/
|
|
183
|
+
const PROMPT_OVERHEAD_RESERVE_CHARS = 50_000;
|
|
184
|
+
// Clamp integrity: the capture ceiling must be >= the projection floor so the
|
|
185
|
+
// derived budget's clamp(raw, FLOOR, CEILING) range is well-formed and the
|
|
186
|
+
// captured excerpt is always a superset of any projection (C4). A module-load
|
|
187
|
+
// assert fails fast on a future mis-edit of these constants.
|
|
188
|
+
if (DOCUMENT_EXCERPT_PROJECTION_FLOOR > DOCUMENT_CAPTURE_CEILING_CHARS) {
|
|
189
|
+
throw new Error("Invalid document excerpt budgets: DOCUMENT_EXCERPT_PROJECTION_FLOOR " +
|
|
190
|
+
`(${DOCUMENT_EXCERPT_PROJECTION_FLOOR}) must be <= ` +
|
|
191
|
+
`DOCUMENT_CAPTURE_CEILING_CHARS (${DOCUMENT_CAPTURE_CEILING_CHARS}).`);
|
|
192
|
+
}
|
|
135
193
|
const TEXT_READABLE_DOCUMENT_EXTENSIONS = new Set([".md", ".txt", ".adoc"]);
|
|
194
|
+
// M3a — explicit positive allowlist of real source-language extensions that earn whole-file
|
|
195
|
+
// capture/projection. The classifier also maps config/data extensions to `code`
|
|
196
|
+
// (.json/.yaml/.yml/.toml/.xml/.env/.cfg/.conf/.lock); those are deliberately NOT here and
|
|
197
|
+
// default to the bounded structural sample (a quality + cost volume reduction — NOT a
|
|
198
|
+
// secret-exposure closure, which is Track 2). Enumerated, no wildcard, so adding a language is
|
|
199
|
+
// a deliberate edit; fail-safe is bounded for any code extension not listed.
|
|
200
|
+
const CODE_WHOLE_CAPTURE_EXTENSIONS = new Set([
|
|
201
|
+
".c",
|
|
202
|
+
".cc",
|
|
203
|
+
".cpp",
|
|
204
|
+
".cs",
|
|
205
|
+
".css",
|
|
206
|
+
".dockerfile",
|
|
207
|
+
".go",
|
|
208
|
+
".graphql",
|
|
209
|
+
".java",
|
|
210
|
+
".js",
|
|
211
|
+
".jsx",
|
|
212
|
+
".kt",
|
|
213
|
+
".mjs",
|
|
214
|
+
".php",
|
|
215
|
+
".prisma",
|
|
216
|
+
".proto",
|
|
217
|
+
".py",
|
|
218
|
+
".rb",
|
|
219
|
+
".rs",
|
|
220
|
+
".scss",
|
|
221
|
+
".sh",
|
|
222
|
+
".swift",
|
|
223
|
+
".ts",
|
|
224
|
+
".tsx",
|
|
225
|
+
]);
|
|
226
|
+
// Extensionless build-language source basenames the classifier maps to `code` by name
|
|
227
|
+
// (CODE_BASENAMES). These are real source/build scripts → whole capture. The config/data
|
|
228
|
+
// basenames (package.json/tsconfig.json/cargo.toml/go.mod/pom.xml) are deliberately NOT here
|
|
229
|
+
// — they default to the bounded sample like other config/data (M3a-default), and are small
|
|
230
|
+
// enough that the sample is the whole file anyway.
|
|
231
|
+
const CODE_WHOLE_CAPTURE_BASENAMES = new Set([
|
|
232
|
+
"dockerfile",
|
|
233
|
+
"makefile",
|
|
234
|
+
"rakefile",
|
|
235
|
+
"gemfile",
|
|
236
|
+
]);
|
|
237
|
+
/**
|
|
238
|
+
* Single whole-capture / full-excerpt eligibility predicate (M3a). Consumed by BOTH the
|
|
239
|
+
* capture owner (`structuralExcerptCharLimit` here) and the seed-stage prompt projection
|
|
240
|
+
* (`isFullExcerptProjectionEligible` in run.ts), so capture and projection can never disagree
|
|
241
|
+
* — a bounded capture under a whole-projection budget would silently author the seed from a
|
|
242
|
+
* partial file. Takes the source REF (path/filename) so it is basename-aware: source-language
|
|
243
|
+
* code (allowlisted extension OR build-language basename) and text-readable documents
|
|
244
|
+
* (.md/.txt/.adoc) earn whole capture; binary documents, config/data code files, and
|
|
245
|
+
* structural-inventory kinds (spreadsheet/database) stay bounded.
|
|
246
|
+
*/
|
|
247
|
+
export function isFullExcerptCaptureEligible(kind, ref) {
|
|
248
|
+
const lower = typeof ref === "string" ? ref.toLowerCase() : "";
|
|
249
|
+
const ext = path.extname(lower);
|
|
250
|
+
const base = path.basename(lower);
|
|
251
|
+
if (kind === "code") {
|
|
252
|
+
return CODE_WHOLE_CAPTURE_EXTENSIONS.has(ext) ||
|
|
253
|
+
CODE_WHOLE_CAPTURE_BASENAMES.has(base);
|
|
254
|
+
}
|
|
255
|
+
return kind === "document" && isTextReadableDocumentExtension(ext);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Derives the seed-stage document projection budget (chars) for the active
|
|
259
|
+
* (provider, model) seat from its registered context window. Pure and total
|
|
260
|
+
* (never throws): an unresolved provider/model, an unregistered pair, or an entry
|
|
261
|
+
* without a window all fall back to the static FLOOR, so mock realization and
|
|
262
|
+
* provider-only seats are unchanged.
|
|
263
|
+
*
|
|
264
|
+
* The route key is the MODEL provider (the registry key, e.g. "openai"), not the
|
|
265
|
+
* runtime adapter provider (openai OAuth dispatches as "codex"; anthropic OAuth
|
|
266
|
+
* stays "anthropic"). The caller passes the model provider so the default
|
|
267
|
+
* gpt-5.5 OAuth seat resolves against `openai/gpt-5.5` (see reconstruct-api).
|
|
268
|
+
*
|
|
269
|
+
* This is the SINGLE model→budget conversion point: model literals never reach
|
|
270
|
+
* the char-budget tuning constants, so G2/INV-CFG-1 stay satisfied.
|
|
271
|
+
*/
|
|
272
|
+
export function deriveDocumentExcerptProjectionBudget(route, registry) {
|
|
273
|
+
if (!route.provider || !route.modelId) {
|
|
274
|
+
return DOCUMENT_EXCERPT_PROJECTION_FLOOR;
|
|
275
|
+
}
|
|
276
|
+
const window = registry.supported_models.find((entry) => entry.provider === route.provider && entry.model === route.modelId)?.context_window_tokens;
|
|
277
|
+
if (!window)
|
|
278
|
+
return DOCUMENT_EXCERPT_PROJECTION_FLOOR;
|
|
279
|
+
const raw = Math.floor(window * WINDOW_BUDGET_FRACTION * CHARS_PER_TOKEN_LB) -
|
|
280
|
+
PROMPT_OVERHEAD_RESERVE_CHARS;
|
|
281
|
+
return Math.min(Math.max(raw, DOCUMENT_EXCERPT_PROJECTION_FLOOR), DOCUMENT_CAPTURE_CEILING_CHARS);
|
|
282
|
+
}
|
|
136
283
|
/**
|
|
137
284
|
* A text-readable document earns the whole-document excerpt budget. Both the capture
|
|
138
285
|
* (here) and the seed-stage prompt projection (run.ts) consult this single predicate so
|
|
@@ -144,8 +291,14 @@ export function isTextReadableDocumentExtension(extension) {
|
|
|
144
291
|
TEXT_READABLE_DOCUMENT_EXTENSIONS.has(extension.toLowerCase()));
|
|
145
292
|
}
|
|
146
293
|
function structuralExcerptCharLimit(kind, ref) {
|
|
147
|
-
|
|
148
|
-
|
|
294
|
+
// Whole-capture eligibility is the single shared predicate (M3a): a source-language file
|
|
295
|
+
// or a text-readable document is projected whole into seed authoring, so it must be
|
|
296
|
+
// CAPTURED whole too — a small leading sample would make seed authoring read only the
|
|
297
|
+
// file's head while the prompt-projection truncation never fires (capture < projection
|
|
298
|
+
// budget), silently authoring the seed from a partial file. Config/data code files,
|
|
299
|
+
// binary documents, and structural-inventory kinds (spreadsheet/database) stay bounded.
|
|
300
|
+
if (isFullExcerptCaptureEligible(kind, ref)) {
|
|
301
|
+
return DOCUMENT_CAPTURE_CEILING_CHARS;
|
|
149
302
|
}
|
|
150
303
|
return DEFAULT_STRUCTURAL_EXCERPT_CHAR_LIMIT;
|
|
151
304
|
}
|
|
@@ -170,7 +323,15 @@ async function textStats(ref, excerptLimit) {
|
|
|
170
323
|
};
|
|
171
324
|
}
|
|
172
325
|
}
|
|
173
|
-
export async function buildReconstructSourceObservation(detection, lineage
|
|
326
|
+
export async function buildReconstructSourceObservation(detection, lineage,
|
|
327
|
+
// Defect-3 basis A: the caller asserts whether this ref is the user-provided
|
|
328
|
+
// reconstruct runtime target. Set true ONLY by the initial-target
|
|
329
|
+
// materialization; frontier-discovered / maturation-closure re-entry callers
|
|
330
|
+
// omit it (conservative default false). Every observation literal below sets
|
|
331
|
+
// is_runtime_target_source from this so source-safety can grant the
|
|
332
|
+
// material_claim/public_output tiers by provenance without leaking to
|
|
333
|
+
// non-target sources.
|
|
334
|
+
options) {
|
|
174
335
|
if (!detection.exists || !isConcreteTargetMaterialKind(detection.kind)) {
|
|
175
336
|
return null;
|
|
176
337
|
}
|
|
@@ -193,6 +354,17 @@ export async function buildReconstructSourceObservation(detection, lineage) {
|
|
|
193
354
|
return null;
|
|
194
355
|
throw error;
|
|
195
356
|
}
|
|
357
|
+
// Spreadsheet sources route through the shared deterministic structure
|
|
358
|
+
// observer instead of the generic raw-text path (design S1 §2.2 / §11).
|
|
359
|
+
if (detection.kind === "spreadsheet") {
|
|
360
|
+
return buildSpreadsheetSourceObservation({
|
|
361
|
+
detection,
|
|
362
|
+
stat,
|
|
363
|
+
location,
|
|
364
|
+
lineage,
|
|
365
|
+
isRuntimeTargetSource: Boolean(options?.isRuntimeTargetSource),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
196
368
|
const stats = stat.isFile()
|
|
197
369
|
? await textStats(detection.ref, structuralExcerptCharLimit(detection.kind, detection.ref))
|
|
198
370
|
: {
|
|
@@ -210,6 +382,7 @@ export async function buildReconstructSourceObservation(detection, lineage) {
|
|
|
210
382
|
round_id: lineage?.roundId ?? "initial_source_frontier",
|
|
211
383
|
observation_batch_id: lineage?.observationBatchId ?? "source-observation-batch:initial",
|
|
212
384
|
triggering_frontier_validation_ref: lineage?.triggeringFrontierValidationRef ?? null,
|
|
385
|
+
is_runtime_target_source: Boolean(options?.isRuntimeTargetSource),
|
|
213
386
|
target_material_kind: detection.kind,
|
|
214
387
|
adapter_id: `minimal-${detection.kind}-structure-observer`,
|
|
215
388
|
source_ref: detection.ref,
|
|
@@ -233,6 +406,96 @@ export async function buildReconstructSourceObservation(detection, lineage) {
|
|
|
233
406
|
}
|
|
234
407
|
return observation;
|
|
235
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Observe a spreadsheet source through the shared structure observer (design S1
|
|
411
|
+
* §2.2) — a deterministic, LLM-free structural inventory. The observation carries
|
|
412
|
+
* NO raw cell values: it never emits the generic path's `content_excerpt` (which for
|
|
413
|
+
* a workbook would be raw data values) and the inventory's aggregate-only vocab
|
|
414
|
+
* leaves `top_values` absent. The one NARROWED exception (design-C, not raw data) is
|
|
415
|
+
* `data_validations[].members` — the bounded, DECLARED type=list enum labels parsed from a
|
|
416
|
+
* formula1 literal (never observed values). `content_sha256` is the RAW-byte hash (§11 HASH-1)
|
|
417
|
+
* surfaced at the structural_data top level because the downstream source-scout-pack
|
|
418
|
+
* reads it there; the full inventory is nested under `workbook_inventory` as the
|
|
419
|
+
* structural substrate the seed-authoring prompt observes. xlsx-family kinds are
|
|
420
|
+
* not yet extractable (P4) and arrive here carrying an `unsupported_reason`.
|
|
421
|
+
*/
|
|
422
|
+
/**
|
|
423
|
+
* The honest observation summary for a workbook inventory (P6): the base disclosure
|
|
424
|
+
* plus the fixed phrases the boundary gate asserts when the capture/macro flags are
|
|
425
|
+
* set. This is the single emit path; it and the gate bind to the SAME exported phrase
|
|
426
|
+
* consts, so assert + emit cannot drift. Exported for the gate-binding regression test.
|
|
427
|
+
*/
|
|
428
|
+
export function buildSpreadsheetObservationSummary(basename, inventory) {
|
|
429
|
+
const parts = [
|
|
430
|
+
inventory.unsupported_reason
|
|
431
|
+
? `spreadsheet workbook observed at ${basename} — extraction unsupported (${inventory.unsupported_reason}), structure_inspected_only`
|
|
432
|
+
: `spreadsheet workbook observed at ${basename} — ${inventory.sheets.length} sheet(s), structure_inspected_only`,
|
|
433
|
+
];
|
|
434
|
+
if (inventory.capture_truncated)
|
|
435
|
+
parts.push(SPREADSHEET_CAPTURE_TRUNCATED_PHRASE);
|
|
436
|
+
if (inventory.macro_present)
|
|
437
|
+
parts.push(SPREADSHEET_MACRO_PRESENT_PHRASE);
|
|
438
|
+
return parts.join("; ");
|
|
439
|
+
}
|
|
440
|
+
async function buildSpreadsheetSourceObservation(args) {
|
|
441
|
+
const { detection, stat, location, lineage, isRuntimeTargetSource } = args;
|
|
442
|
+
const basename = path.basename(detection.ref);
|
|
443
|
+
const extension = path.extname(detection.ref).toLowerCase();
|
|
444
|
+
// Route through the single shared projection: the structural_data inventory
|
|
445
|
+
// carries aggregate counts / structure only, never raw cell values (the sole narrowed
|
|
446
|
+
// exception is data_validations[].members — bounded declared type=list enum labels).
|
|
447
|
+
const inventory = projectInventoryForAdmission(await observeSpreadsheetSource(detection.ref));
|
|
448
|
+
const summary = buildSpreadsheetObservationSummary(basename, inventory);
|
|
449
|
+
const observationId = stableObservationId({ sourceRef: detection.ref, location });
|
|
450
|
+
// P1-C1 §5.7: the ComprehensionArtifact COMPANIONS the inventory (joined by observation_id) — a
|
|
451
|
+
// deterministic-only edition in this cut (LLM-touch fields are explicit not_applicable). It is
|
|
452
|
+
// validated by validateSourceObservationBoundary below (construction throws on an invalid contract).
|
|
453
|
+
const comprehensionArtifact = buildDeterministicComprehensionArtifact({
|
|
454
|
+
observationId,
|
|
455
|
+
inventory,
|
|
456
|
+
});
|
|
457
|
+
const observation = {
|
|
458
|
+
observation_id: observationId,
|
|
459
|
+
round_id: lineage?.roundId ?? "initial_source_frontier",
|
|
460
|
+
observation_batch_id: lineage?.observationBatchId ?? "source-observation-batch:initial",
|
|
461
|
+
triggering_frontier_validation_ref: lineage?.triggeringFrontierValidationRef ?? null,
|
|
462
|
+
is_runtime_target_source: isRuntimeTargetSource,
|
|
463
|
+
target_material_kind: "spreadsheet",
|
|
464
|
+
adapter_id: SPREADSHEET_OBSERVER_ADAPTER_ID,
|
|
465
|
+
source_ref: detection.ref,
|
|
466
|
+
location,
|
|
467
|
+
summary,
|
|
468
|
+
structural_data: {
|
|
469
|
+
basename,
|
|
470
|
+
extension: extension || null,
|
|
471
|
+
path_kind: stat.isDirectory() ? "directory" : "file",
|
|
472
|
+
size_bytes: stat.isFile() ? stat.size : null,
|
|
473
|
+
// Raw-byte hash (§11 HASH-1) surfaced top-level for source-scout-pack
|
|
474
|
+
// admission, which reads structural_data.content_sha256.
|
|
475
|
+
content_sha256: inventory.content_sha256,
|
|
476
|
+
workbook_inventory: inventory,
|
|
477
|
+
comprehension_artifact: comprehensionArtifact,
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
const validation = validateSourceObservationBoundary(observation);
|
|
481
|
+
if (!validation.valid) {
|
|
482
|
+
throw new Error(`Invalid source observation boundary for ${detection.ref}: ${validation.violations.join("; ")}`);
|
|
483
|
+
}
|
|
484
|
+
return observation;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* The `unsupported_reason` of a spreadsheet observation's workbook inventory, or
|
|
488
|
+
* null when the observation is not an unsupported workbook. An unsupported workbook
|
|
489
|
+
* (.xls/.xlsb/.ods, corrupt, oversized) carries no structural evidence, so the
|
|
490
|
+
* materialize loop demotes it to a skip rather than admitting an empty observation.
|
|
491
|
+
*/
|
|
492
|
+
export function spreadsheetUnsupportedReason(observation) {
|
|
493
|
+
if (observation.target_material_kind !== "spreadsheet")
|
|
494
|
+
return null;
|
|
495
|
+
const inventory = observation.structural_data.workbook_inventory;
|
|
496
|
+
const reason = inventory?.unsupported_reason;
|
|
497
|
+
return typeof reason === "string" && reason.length > 0 ? reason : null;
|
|
498
|
+
}
|
|
236
499
|
function stableFrontierRefId(unit) {
|
|
237
500
|
const digest = crypto
|
|
238
501
|
.createHash("sha256")
|
|
@@ -357,9 +620,32 @@ export async function materializeReconstructPreparationArtifacts(params) {
|
|
|
357
620
|
const refDetection = detection.per_ref.find((candidate) => candidate.ref === unit.ref);
|
|
358
621
|
if (!refDetection)
|
|
359
622
|
continue;
|
|
360
|
-
|
|
623
|
+
// Defect-3 basis A: these are the user-provided reconstruct runtime-target
|
|
624
|
+
// refs (the initial target inventory), so they are runtime-target sources.
|
|
625
|
+
const observation = await buildReconstructSourceObservation(refDetection, undefined, {
|
|
626
|
+
isRuntimeTargetSource: true,
|
|
627
|
+
});
|
|
361
628
|
if (observation) {
|
|
362
|
-
|
|
629
|
+
const unsupportedReason = spreadsheetUnsupportedReason(observation);
|
|
630
|
+
if (unsupportedReason) {
|
|
631
|
+
// A workbook whose format the observer cannot extract (.xls/.xlsb/.ods,
|
|
632
|
+
// corrupt, oversized) yields an inventory carrying only `unsupported_reason`
|
|
633
|
+
// — no structural evidence. After the gate flip such a ref is runnable and
|
|
634
|
+
// produces an observation, so without this demotion a sole legacy-format
|
|
635
|
+
// target would pass the zero-observation halt and reach LLM authoring with
|
|
636
|
+
// empty evidence. Demote it to a skip so the run fails loud as unsupported
|
|
637
|
+
// (the evidence gate stays honest about un-observed material).
|
|
638
|
+
unit.scan_status = "skipped";
|
|
639
|
+
unit.skip_reason = `spreadsheet extraction unsupported: ${unsupportedReason}`;
|
|
640
|
+
skippedRefs.push({
|
|
641
|
+
ref: unit.ref,
|
|
642
|
+
target_material_kind: unit.target_material_kind,
|
|
643
|
+
reason: unit.skip_reason,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
observations.push(observation);
|
|
648
|
+
}
|
|
363
649
|
}
|
|
364
650
|
else {
|
|
365
651
|
// buildReconstructSourceObservation returns null when the ref is no longer
|