@c4a/context 0.6.11 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ export declare const CODE_INDEX_MODULE_TYPES: readonly ["api-service", "service", "background-runtime", "sdk-library", "web-application", "adapter", "cli-tool", "monorepo-container", "contract-source", "derived-source", "unknown"];
2
+ export type CodeIndexModuleType = typeof CODE_INDEX_MODULE_TYPES[number];
3
+ export declare const CODE_INDEX_MODULE_FACETS: readonly ["page-routing", "public-api", "protocol-provider", "protocol-consumer", "event-producer", "event-consumer", "persistence", "plugin-extension", "configuration-runtime", "build-release", "cross-module-chain", "generated-contract"];
4
+ export type CodeIndexModuleFacet = typeof CODE_INDEX_MODULE_FACETS[number];
5
+ export declare const CODE_INDEX_OUTPUT_PROFILES: readonly ["protocol-index", "service-boundary", "runtime-map", "public-api-reference", "module-map", "application-map", "adapter-contract", "command-map", "module-registry", "cross-module-flow", "provenance-only"];
6
+ export type CodeIndexOutputProfile = typeof CODE_INDEX_OUTPUT_PROFILES[number];
7
+ export declare const CODE_INDEX_COVERAGE_KINDS: readonly ["responsibility", "entrypoint", "operation", "contract", "handoff", "state-boundary", "failure-recovery", "delivery", "source-authority"];
8
+ export type CodeIndexCoverageKind = typeof CODE_INDEX_COVERAGE_KINDS[number];
9
+ export declare function requiredCodeIndexCoverage(input: {
10
+ outputProfile: CodeIndexOutputProfile;
11
+ facets?: readonly CodeIndexModuleFacet[];
12
+ }): CodeIndexCoverageKind[];
13
+ export declare const CODE_INDEX_CAPABILITIES: readonly ["complete", "project-adapter", "material-required"];
14
+ export type CodeIndexCapability = typeof CODE_INDEX_CAPABILITIES[number];
15
+ export declare const CODE_INDEX_LIFECYCLES: readonly ["authoritative", "generated", "mirrored", "legacy", "vendored"];
16
+ export type CodeIndexLifecycle = typeof CODE_INDEX_LIFECYCLES[number];
17
+ /** Stable knowledge intent for one user-visible code index unit. */
18
+ export interface CodeIndexUnitPlan {
19
+ id: string;
20
+ inputSources: readonly string[];
21
+ outputOwner: string;
22
+ /** Primary classification retained for compact reports and existing plans. */
23
+ moduleType: CodeIndexModuleType;
24
+ /** All applicable module archetypes. A hybrid module may declare more than one. */
25
+ moduleTypes?: readonly CodeIndexModuleType[];
26
+ /** Composable behavior and contract facets used to select extraction guidance. */
27
+ facets?: readonly CodeIndexModuleFacet[];
28
+ /** Source-backed reasons for the selected module type. */
29
+ moduleTypeEvidence?: readonly string[];
30
+ outputProfile: CodeIndexOutputProfile;
31
+ responsibility: string;
32
+ entries: readonly string[];
33
+ pageKinds: readonly string[];
34
+ protocols: readonly string[];
35
+ dependencies: readonly string[];
36
+ exclusions: readonly string[];
37
+ lifecycle?: CodeIndexLifecycle;
38
+ sourceOfTruth?: string;
39
+ capability: CodeIndexCapability;
40
+ }
41
+ export type CodeIndexInspectionFindingKind = "module" | "entry" | "protocol" | "dependency" | "lifecycle" | "source-of-truth";
42
+ export interface CodeIndexInspectionFinding {
43
+ indexUnitId: string;
44
+ source: string;
45
+ kind: CodeIndexInspectionFindingKind;
46
+ summary: string;
47
+ path?: string;
48
+ }
49
+ export interface CodeIndexCapabilityGap {
50
+ indexUnitId: string;
51
+ reason: string;
52
+ requestedMaterial?: string;
53
+ }
54
+ export interface CodeIndexInspectionResult {
55
+ findings: readonly CodeIndexInspectionFinding[];
56
+ capabilityGaps?: readonly CodeIndexCapabilityGap[];
57
+ }
58
+ export interface CodeIndexInspectionContext {
59
+ projectRoot: string;
60
+ sources: readonly {
61
+ name: string;
62
+ materializedAt: string;
63
+ absolutePath: string;
64
+ }[];
65
+ }
66
+ export type CodeIndexInspectionAdapter = (context: CodeIndexInspectionContext) => CodeIndexInspectionResult | Promise<CodeIndexInspectionResult>;
67
+ export declare const NO_ENTRY_DETECTED: "NO_ENTRY_DETECTED";
68
+ export declare class ExtractTsConfigurationError extends TypeError {
69
+ readonly code: "NO_ENTRY_DETECTED";
70
+ constructor(message: string);
71
+ }
72
+ export declare function normalizeExtractEntry(value: string): string;
73
+ export declare function normalizeIndexUnit(unit: CodeIndexUnitPlan, field: string): CodeIndexUnitPlan;
74
+ export declare function assertUniqueIndexUnits(units: readonly CodeIndexUnitPlan[], field: string): void;
@@ -89,26 +89,20 @@ package needs project-specific terminology, entry points, known limits, or
89
89
  task workflows. Authors may explicitly accept the generic default when it is
90
90
  intentionally sufficient.
91
91
 
92
- When approved pages reference materialized document resources, Context keeps
93
- their production copies in content-addressed `knowledge/assets/` paths. New KB
94
- setup should offer Git raw delivery first. It rewrites package links to either
95
- an automatically derived raw prefix or an explicit author-provided prefix.
96
- Context does not check whether resource files are committed, pushed, or
97
- remotely readable; that is the package author's responsibility. With no
98
- explicit prefix, supported remotes use an immutable commit URL. An explicit
99
- prefix can contain `{commit}` when the Context workspace is in Git, or name a
100
- mutable branch when that lifecycle is intentional; a literal prefix also works
101
- when resources are published from a separate repository.
102
-
103
- Workspaces without usable Git or an explicit raw prefix can choose bundled
104
- delivery, which copies only selected resources to `others/assets/`, or explicit
105
- omission, which does not copy resources and reports that links remain
106
- unresolved. Bundled output may
107
- optionally install `sharp` in the workspace and configure
108
- `assets.optimize`; Context itself has no image dependency. Optimization changes
109
- only `dist/`, content-addresses smaller WebP output, and leaves `sources/` and
110
- `knowledge/assets/` unchanged. Source audit XML and capture reports are never
111
- distributed as reader assets. See [Lark Resource Materialization](./lark-resources.md).
92
+ When approved pages reference materialized resources, Context keeps their
93
+ production copies in content-addressed `knowledge/assets/` paths and bundles
94
+ selected resources into `others/assets/` by default. Supported images are
95
+ adaptively compressed only when needed: every packaged image must be at most 1
96
+ MiB and all packaged images together must be at most 40 MiB. The CLI owns the
97
+ processor. Optimization changes only `dist/`, content-addresses smaller WebP
98
+ output, and leaves `sources/` and `knowledge/assets/` unchanged.
99
+
100
+ Configure Git raw delivery only when external immutable links are an explicit
101
+ project requirement. Context can derive supported Git URLs or use an explicit
102
+ HTTPS `urlPrefix`, but it does not publish or probe those resources. Explicit
103
+ omission remains available and reports unresolved links. Source audit XML and
104
+ capture reports are never distributed as reader assets. See
105
+ [Lark Resource Materialization](./lark-resources.md).
112
106
 
113
107
  The same inventory exposes `structure.relationship_coverage`. It records
114
108
  whether selected codegraph pages have current source-backed AST relationship
@@ -6,8 +6,9 @@ chooses how those facts become source-backed code knowledge.
6
6
 
7
7
  ## Inspect Before Declaring
8
8
 
9
- Run every read-only inspection command returned by the extraction-scope Gate.
10
- The result identifies each confirmed module and its recognized `manifests`.
9
+ Run the single batch inspection command returned by the extraction-scope Gate.
10
+ The result identifies every confirmed module, its recognized `manifests`,
11
+ README locations, entry candidates, protocol locators, and lifecycle markers.
11
12
  Treat these as deterministic technology signals, not as product semantics:
12
13
 
13
14
  | Signal | Technology candidate |
@@ -43,6 +44,19 @@ Current reusable capabilities are:
43
44
  | React Router route declarations | `extractReactRouterRoutes()` from `@c4a/extract-ts` | call from `extractCustom()`; complements TypeScript symbols |
44
45
  | Rust, Python, Java/JVM, or an unsupported framework/protocol | no assumed built-in parser | project-owned `extractCustom()` adapter |
45
46
 
47
+ The custom extraction preview verifies this selection mechanically. Context
48
+ detects applicable community capabilities from source manifests and stable path
49
+ signals, then checks that candidate evidence covers every required entry,
50
+ route, implementation boundary, workspace, or protocol probe. One aggregated
51
+ module page is valid when it closes that structural coverage. A callback that
52
+ only hashes a few filenames or renders configured prose does not satisfy the
53
+ probe, even when its Markdown count is small.
54
+
55
+ The probe does not assign business meaning and does not require one page per
56
+ fact. The project adapter still owns grouping, titles, explanations, and
57
+ cross-module semantics. If the source uses an unsupported language or protocol,
58
+ report a capability gap instead of claiming that a known probe was consumed.
59
+
46
60
  An optional package does not create a new CLI phase. Add it as an explicit
47
61
  workspace dependency, then map its structural facts to candidates in the
48
62
  project callback. Do not add a parser package when its documented coverage does
@@ -50,23 +64,18 @@ not match the inspected source.
50
64
 
51
65
  ## Read The Contract Before Extending
52
66
 
53
- Before editing `src/index.ts`, read the relevant installed public manual or
54
- package README. Do not infer APIs from bundled JavaScript.
55
-
56
- - Context lifecycle and `extractCustom()`:
57
- `node_modules/@c4a/context/docs/reference/project-api.md`
58
- - Generic plugin protocol:
59
- `node_modules/@c4a/extract/README.md`
60
- - TypeScript:
61
- `node_modules/@c4a/extract-ts/README.md`
62
- - Go:
63
- `node_modules/@c4a/extract-go/README.md`
64
- - Rush:
65
- `node_modules/@c4a/extract-rush/README.md`
67
+ Before editing `src/index.ts`, read the Route-selected Context lifecycle and
68
+ extractor resources completely. They are the installed contract for
69
+ Context-owned phases such as `extractTs()`; do not require a separate
70
+ workspace copy of an implementation package and do not infer APIs from bundled
71
+ JavaScript.
66
72
 
67
- If an optional package is not installed, use this capability matrix to decide
68
- whether it is relevant, add only that dependency, and then read its shipped
69
- README before implementing the callback.
73
+ Only a capability imported directly by a project-owned `extractCustom()`
74
+ adapter requires its package README. Use this matrix to decide whether that
75
+ optional capability is relevant, add only that dependency, then read the
76
+ README from the resolved installed package before implementing the callback.
77
+ Never assume that a transitive or dev-only package is present at a hard-coded
78
+ `node_modules` path.
70
79
 
71
80
  A project-owned adapter may use an existing parser, compiler API, or command
72
81
  whose output is deterministic. It must return source-backed candidates through
@@ -83,6 +92,54 @@ Before the first extraction preview, state briefly:
83
92
  - whether coverage is complete or which facts remain project-owned; and
84
93
  - why another available extractor is not needed.
85
94
 
95
+ After preview, use `inspection.structuralProbes` and each index unit's
96
+ `structuralCoverage` as the exact audit result. An uncovered probe is a
97
+ configuration problem, not a Review decision.
98
+
86
99
  If no current capability can parse the source reliably, stop at configuration
87
100
  and report the missing generic capability. Do not silently emit an empty
88
101
  codegraph or reuse an unrelated parser.
102
+
103
+ ## Plan Before Parsing
104
+
105
+ Classify the user-visible module before selecting language tooling or reading an
106
+ archetype template: API/service, background runtime, SDK/library, interactive
107
+ application, adapter, CLI/tool, monorepo container, derived source,
108
+ authoritative contract source, or unknown.
109
+ A hybrid module may declare several `moduleTypes` and several behavior `facets`;
110
+ keep one primary `moduleType` for concise reports. Record inspected paths in
111
+ `moduleTypeEvidence`, then read all matching Route-recommended files below
112
+ `resources/semantic/code-index/templates/` and combine them into one plan.
113
+ After that, choose exactly one closed output profile: `module-map`,
114
+ `application-map`, `protocol-index`, `service-boundary`, `runtime-map`,
115
+ `public-api-reference`, `command-map`, `adapter-contract`, `module-registry`,
116
+ `cross-module-flow`, or `provenance-only`. The profile selects structural probes
117
+ and advisory checks; an invented value is rejected.
118
+
119
+ Each archetype resource is a working template for an Agent with limited prior
120
+ context. It provides a minimum evidence pass, the reader questions the index
121
+ must answer, suggested knowledge units, Markdown chapter blueprints,
122
+ aggregation and relationship rules, composition examples, and stop conditions.
123
+ The blueprints are illustrative: omit unsupported sections and merge overlap
124
+ across selected templates instead of producing empty headings or duplicate
125
+ pages. They shape content before the batch preview; they do not prescribe or
126
+ override projected page counts.
127
+
128
+ Extractor shape defines what can be emitted. `extractTs()` creates one page per
129
+ selected symbol and permits one owning index unit per source. Use it for an
130
+ intentional granular public reference. Use `extractCustom()` for module-level
131
+ aggregation, registries, protocol indexes, cross-module flows, or multiple
132
+ candidate owners over one source; each candidate declares its `module` and
133
+ evidence-scoped `sections`. Each section's typed coverage and exact evidence is
134
+ checked against the output profile during preview. Resolve repositories from
135
+ the extractor context's `sources[].absolutePath`, never from a
136
+ machine-specific checkout path. Cross-module flow output must also emit
137
+ source-backed structured edges. Generated clients/models, mirrored sources, legacy
138
+ implementations, and internal helpers should normally be excluded or recorded
139
+ as provenance rather than expanded one symbol per page.
140
+
141
+ If a repository uses service manifests or protocol registrations that the
142
+ community inspector cannot interpret, keep that interpretation in a generic
143
+ project-owned `inspect` adapter attached to `extractCustom()`. Return findings
144
+ and capability gaps through the public Context contract; do not add internal
145
+ framework names or directory rules to the CLI.
@@ -31,10 +31,7 @@ kbPackage({
31
31
  foldDirectoryIndexes: true,
32
32
  maxInlineEntries: 50,
33
33
  },
34
- assets: {
35
- delivery: "git-raw",
36
- urlPrefix: "https://code.example.com/team/knowledge/raw/{commit}",
37
- },
34
+ assets: { delivery: "bundle" },
38
35
  });
39
36
  ```
40
37
 
@@ -56,10 +53,16 @@ llmsPackage({
56
53
  | `template` | yes | Project-relative template directory or `{ path, vars }`. |
57
54
  | `select` | no | Approved knowledge selector. Omit to include all approved knowledge. Supports internal `collections`, OKF `okfRoots`, and `include` / `exclude` path patterns relative to `knowledge/`. |
58
55
  | `navigation` | no | KB directory-index policy. Defaults to `{ foldDirectoryIndexes: true, maxInlineEntries: 50 }`. |
59
- | `assets` | no | Resource delivery: Git raw links, bundled files, or explicit omission. New KB setup should offer Git raw first. Omit for legacy byte-for-byte bundling. |
56
+ | `assets` | no | Resource delivery: bundled files by default, explicit Git raw links, or explicit omission. |
60
57
  | `distribution` | no | Legacy input accepted from older workspaces. It no longer changes package paths and should not be added to new declarations. |
61
58
 
62
- Use Git raw delivery when resources are published from a Git repository.
59
+ Bundled delivery is the default. Selected resources are written below
60
+ `others/assets/`. Supported images are automatically compressed when required
61
+ to keep each image at or below 1 MiB and all package images at or below 40 MiB.
62
+ The source snapshots and approved `knowledge/assets/` files are never changed.
63
+
64
+ Use Git raw delivery only when it is explicitly configured for resources
65
+ published from a Git repository.
63
66
  Without `urlPrefix`, the Context workspace must be inside Git; GitHub remotes
64
67
  are derived automatically and pinned to the current commit. Other hosts accept
65
68
  an explicit HTTPS prefix. `{commit}` is replaced only when the workspace is
@@ -70,14 +73,14 @@ appends the project-relative `knowledge/assets/...` path.
70
73
  For a non-GitHub service that exposes raw files through the GitHub-compatible
71
74
  same-host layout
72
75
  `https://<host>/<namespace>/<repository>/raw/<ref>/<path>`, derive and configure
73
- the explicit prefix before choosing bundled delivery. For example, use
76
+ the explicit prefix before choosing Git raw delivery. For example, use
74
77
  `https://git.example.com/team/knowledge/raw/{commit}` for a workspace at the
75
78
  repository root. If the workspace is nested, append its repository-relative
76
79
  directory to the prefix because Context appends only the project-relative
77
80
  `knowledge/assets/...` path. This derivation is appropriate only when the
78
81
  service's raw convention and repository identity are confirmed; otherwise ask
79
- for the prefix or use bundled delivery. The fact that Context does not
80
- automatically recognize a host is not, by itself, a reason to bundle.
82
+ for the prefix or keep the default bundled delivery. Never invent a raw URL
83
+ only because Context does not automatically recognize a host.
81
84
 
82
85
  The resolved commit and raw URL participate in package freshness, so changing
83
86
  Git HEAD or the selected remote makes an existing package stale. Context does
@@ -108,26 +111,12 @@ assets: { delivery: "bundle" }
108
111
  assets: { delivery: "omit" } // keeps unresolved links and reports them
109
112
  ```
110
113
 
111
- Bundled resources are copied byte-for-byte unless image optimization is
112
- configured. When optimizable PNG/JPEG resources exceed 20 MiB, build and status
113
- return `package.assets.optimization-recommended`. To optimize bundled output:
114
-
115
- ```bash
116
- bun add -D sharp
117
- ```
118
-
119
- ```ts
120
- assets: {
121
- delivery: "bundle",
122
- optimize: { processor: "sharp", mode: "lossless-webp" },
123
- }
124
- ```
125
-
126
- `optimize.mode: "webp"` additionally accepts `quality` from 1 to 100. Both modes accept
114
+ Explicit `assets.optimize` overrides the automatic output policy.
115
+ `optimize.mode: "webp"` accepts `quality` from 1 to 100. Both modes accept
127
116
  an optional positive `maxDimension`; images are never enlarged. Context adopts
128
117
  a generated image only when it is smaller, uses a digest-derived `.webp` path,
129
- and rewrites package links. A configured but missing processor blocks before
130
- the previous `dist/` package is replaced.
118
+ and rewrites package links. If explicit settings cannot meet the package image
119
+ budgets, build stops before replacing the previous `dist/` package.
131
120
 
132
121
  `template` is required. Do not call `kbPackage({ name })` or
133
122
  `llmsPackage({ name })`.
@@ -281,13 +270,17 @@ Default navigation rules:
281
270
 
282
271
  - `wikis/index.md` is the required default bundle index. Other selected OKF
283
272
  roots always use their own `<okf-root>/index.md`.
284
- - With `foldDirectoryIndexes: true`, a non-root directory gets its own
285
- `index.md` only when its descendant knowledge-page count is greater than
286
- `maxInlineEntries`. The default threshold is `50`.
273
+ - With `foldDirectoryIndexes: true`, directory indexes are planned bottom-up.
274
+ A non-root directory gets its own `index.md` only when the page and child-index
275
+ entries it would expose to its parent are greater than `maxInlineEntries`.
276
+ The default threshold is `50`.
287
277
  - A folded directory is not discarded. Its pages are listed in the nearest
288
- generated ancestor index, grouped by their relative directory path.
289
- - The threshold counts selected knowledge pages in the path tree. It does not
290
- inspect Markdown line counts, headings, or content semantics.
278
+ generated ancestor index, grouped by their relative directory path, while
279
+ retained descendant indexes are linked directly from that ancestor. This
280
+ also removes large but navigation-thin intermediate directories.
281
+ - The threshold counts visible navigation entries after descendant indexes are
282
+ planned. It does not inspect Markdown line counts, headings, or content
283
+ semantics.
291
284
  - Set `foldDirectoryIndexes: false` to generate an `index.md` for every
292
285
  directory, matching the fully expanded navigation shape.
293
286
  - A generated directory index uses OKF frontmatter with `type: Knowledge
@@ -606,8 +606,65 @@ Options:
606
606
  | `mode` | `"exports"` (default) traces public exports from automatic or configured entries; `"scan"` uses every file matched by `include` as an entry root |
607
607
  | `entries` | Optional source-relative entry files for `"exports"` mode. They override `package.json` entry detection and live only in the Context project configuration |
608
608
  | `exportedOnly` | Defaults to `true` in `"exports"` mode and `false` in `"scan"` mode |
609
+ | `indexUnits` | Stable module/index plans used for ownership, capability and per-unit scale checks. A single-source exports-only package gets a compatible public-contract default; scan, collection and custom extraction require an explicit plan before formal writes |
609
610
  | `transform` | Optional markdown transform function or functions |
610
611
 
612
+ An explicit index unit records production intent rather than parser settings:
613
+
614
+ ```ts
615
+ extractTs({
616
+ source: componentLib,
617
+ collection: "codegraph",
618
+ indexUnits: [{
619
+ id: "component-public-api",
620
+ inputSources: ["20260712/component-lib"],
621
+ outputOwner: "component-lib",
622
+ moduleType: "sdk-library",
623
+ moduleTypes: ["sdk-library"],
624
+ facets: ["public-api", "plugin-extension"],
625
+ moduleTypeEvidence: ["package.json exports and src/index.ts public entry"],
626
+ outputProfile: "public-api-reference",
627
+ responsibility: "Document stable exported component contracts.",
628
+ entries: ["src/index.ts"],
629
+ pageKinds: ["module-map", "public-contract"],
630
+ protocols: [],
631
+ dependencies: [],
632
+ exclusions: ["src/internal/**", "src/generated/**"],
633
+ lifecycle: "authoritative",
634
+ sourceOfTruth: "src/index.ts",
635
+ capability: "complete",
636
+ }],
637
+ });
638
+ ```
639
+
640
+ `inputSources` names registered evidence sources; `outputOwner` is the one
641
+ stable page owner used for accounting and navigation. `capability` is
642
+ `"complete"`, `"project-adapter"`, or `"material-required"`. The last value
643
+ stops the Route until the plan is narrowed or reliable source material is
644
+ provided.
645
+
646
+ `moduleType` is the primary compact classification. `moduleTypes` may add other
647
+ applicable archetypes for a hybrid module, while `facets` records composable
648
+ behaviors such as routing, protocol consumption, events, persistence, plugins,
649
+ release, or cross-module chains. `moduleTypeEvidence` records the inspected
650
+ paths that support the classification. Classify first, then read the matching
651
+ Route-provided code-index templates, and only then finish the extraction plan.
652
+ `lifecycle` is `"authoritative"`, `"generated"`, `"mirrored"`, `"legacy"`,
653
+ or `"vendored"`; derived sources normally use `"provenance-only"` rather
654
+ than duplicating reader-facing pages. These are generic project facts, not
655
+ framework names inferred by the CLI.
656
+
657
+ `moduleType`, `moduleTypes`, `facets`, `outputProfile`, `lifecycle`, and
658
+ `capability` are runtime-validated closed values. Supported output profiles are
659
+ `module-map`, `application-map`, `protocol-index`, `service-boundary`,
660
+ `runtime-map`, `public-api-reference`, `command-map`, `adapter-contract`,
661
+ `module-registry`, `cross-module-flow`, and `provenance-only`.
662
+
663
+ `extractTs()` projects one candidate page per selected symbol, and each source
664
+ can belong to only one of its index units. Use `extractCustom()` for aggregated
665
+ maps, registries, protocol indexes, cross-module flows, or multiple candidate
666
+ owners over one source; custom candidates declare their owning `module`.
667
+
611
668
  `source` is the only package/module boundary. `include` narrows files inside
612
669
  that source; it does not select a second module. Standard packages can omit
613
670
  `entries` and use `package.json` `exports`, `main`, or `bin` detection. For a
@@ -625,7 +682,9 @@ extractTs({
625
682
 
626
683
  When the intended knowledge scope is every declaration in the selected files
627
684
  rather than a public export graph, use `mode: "scan"`. Scan mode does not accept
628
- `entries`; `include` supplies its file roots.
685
+ `entries`; `include` supplies its file roots. Because scan mode can expand
686
+ internal declarations into a symbol catalog, it requires an explicit
687
+ `indexUnits` plan before formal extraction.
629
688
 
630
689
  Entry failures use the stable machine code `NO_ENTRY_DETECTED`. This includes
631
690
  `entries: []`, exports mode with no detected/configured entry, and scan mode
@@ -657,6 +716,21 @@ does not infer which symbols are meaningful to a particular product or
657
716
  audience. Modules with skipped files include the deterministic traversal
658
717
  reason, such as files not reachable from exports-mode entries.
659
718
 
719
+ Before formal extraction, the workflow runs one batch preview for all pending
720
+ phases. Each `indexUnits[]` result reports projected Markdown pages, output
721
+ profile/owner, content-byte estimates, and risks. Per unit, 0–100 pages is
722
+ normal, 101–300 is a warning that may continue, and more than 300 is blocked.
723
+ The limit is non-delegatable, including in managed mode. A passing preview is
724
+ cached by digest under `.tmp/context-runtime/extract/previews/` and reused by
725
+ formal extraction when the source scope, phase declaration, project `src/`,
726
+ dependency lock, and preview protocol still match. A missing cache is
727
+ recoverable by rerunning the preview. The report includes cache hits,
728
+ extractor invocation count, current and projected page counts, changes,
729
+ exported/internal distribution, top directories, and advisory large-page
730
+ risks; only the 300-page per-unit limit is a hard scale gate.
731
+ A batch-total page advisory and quality risks such as a thin custom aggregate
732
+ remain report signals and do not create another Gate.
733
+
660
734
  Phase id shape:
661
735
 
662
736
  ```text
@@ -700,19 +774,53 @@ extractCustom({
700
774
  id: "extract:service:protocol",
701
775
  sources: [service],
702
776
  collection: "codegraph",
703
- extract: async ({ projectRoot }) => ({
777
+ indexUnits: [{
778
+ id: "service-protocol",
779
+ inputSources: ["20260811/service"],
780
+ outputOwner: "service",
781
+ moduleType: "api-service",
782
+ moduleTypes: ["api-service", "adapter"],
783
+ facets: ["protocol-provider", "protocol-consumer", "cross-module-chain"],
784
+ moduleTypeEvidence: ["src/protocol.ts registration and src/handler.ts dispatch"],
785
+ outputProfile: "protocol-index",
786
+ responsibility: "Document the stable service protocol boundary.",
787
+ entries: ["src/protocol.ts"],
788
+ pageKinds: ["protocol-index"],
789
+ protocols: ["declared service protocol"],
790
+ dependencies: [],
791
+ exclusions: ["generated/**"],
792
+ lifecycle: "authoritative",
793
+ capability: "project-adapter",
794
+ }],
795
+ extract: async ({ sources }) => {
796
+ const serviceRoot = sources.find((item) => item.name === "20260811/service")?.absolutePath;
797
+ if (serviceRoot === undefined) throw new Error("service source is not materialized");
798
+ const protocolEvidence = inspectProtocol(serviceRoot);
799
+ return {
704
800
  candidates: [{
705
801
  nodeRef: "service/protocol",
706
802
  kind: "protocol",
707
803
  visibility: "exported",
708
804
  module: "service",
709
- markdown: renderProtocol(projectRoot),
710
- evidence: [{
711
- source: "20260811/service",
712
- file: "src/protocol.ts",
713
- symbol: "protocol",
714
- kind: "variable",
715
- digest: "0123456789ab",
805
+ evidence: [protocolEvidence],
806
+ sections: [{
807
+ id: "contract",
808
+ kind: "contract",
809
+ title: "Provided contract",
810
+ markdown: renderContract(serviceRoot),
811
+ evidence: [protocolEvidence],
812
+ }, {
813
+ id: "operations",
814
+ kind: "operation",
815
+ title: "Operations",
816
+ markdown: renderOperations(serviceRoot),
817
+ evidence: inspectOperations(serviceRoot),
818
+ }, {
819
+ id: "handoff",
820
+ kind: "handoff",
821
+ title: "Implementation handoff",
822
+ markdown: renderHandoff(serviceRoot),
823
+ evidence: inspectHandoff(serviceRoot),
716
824
  }],
717
825
  review: {
718
826
  title: "Service protocol",
@@ -721,18 +829,50 @@ extractCustom({
721
829
  reason: "Review the project-owned extraction.",
722
830
  },
723
831
  }],
724
- }),
832
+ }},
725
833
  });
726
834
  ```
727
835
 
728
- `sources` is the complete registered repo scope for the phase. Every candidate
729
- and edge carries structured `evidence`; the CLI validates that evidence against
836
+ `sources` is the complete CLI-resolved repo scope for the phase. Resolve files
837
+ from `sources[].absolutePath`; do not embed a local or remote Agent checkout
838
+ path. Every candidate section and edge carries structured `evidence`; the CLI validates that evidence against
730
839
  the declared sources, creates canonical `source_ref` values, writes the symbol
731
840
  index, candidate ledger and Review snapshots atomically, and records a phase
732
- fingerprint. `context status` therefore treats this phase exactly like another
841
+ fingerprint. Evidence-scoped section `kind` values satisfy the selected output
842
+ profile's semantic coverage contract. A cross-module flow also requires at
843
+ least one source-backed structured edge. `context status` therefore treats this phase exactly like another
733
844
  pending code extraction target, and Review can verify snapshot freshness
734
845
  without a placeholder `extractTs` phase.
735
846
 
847
+ `indexUnits` is also the batch scale and ownership contract. Candidate
848
+ `module` must match one declared unit id or output owner. Older callbacks that
849
+ omit `indexUnits` remain compatible: Context groups candidates by `module` and
850
+ marks the plan as inferred. Once explicit units exist, an unmatched or
851
+ multiply-owned candidate blocks formal extraction instead of being guessed.
852
+ An inferred plan can be previewed for migration diagnostics, but formal writes
853
+ require the project to declare stable units and owners.
854
+
855
+ For a large adapter, `candidates` may be an `AsyncIterable` instead of an
856
+ array. Context consumes it incrementally and stops retaining full candidates
857
+ for an index unit after the 301st item proves that the unit is blocked. Array
858
+ callbacks remain supported and are reported as `legacy-preview`.
859
+
860
+ An optional generic `inspect` adapter can return source-backed module, entry,
861
+ protocol, dependency, lifecycle, and source-of-truth findings before candidate
862
+ collection. It may also return capability gaps tied to declared index-unit ids;
863
+ those gaps enter the one non-delegatable capability Gate. Internal framework
864
+ meaning stays in the project adapter and its referenced material.
865
+
866
+ The CLI also runs a lightweight structural probe before every custom preview.
867
+ It recognizes TypeScript symbols, React Router routes, Go symbols, Rush
868
+ workspace structure, and source-owned protocol schemas from generic manifests
869
+ and paths. The preview exposes all detected probes in
870
+ `inspection.structuralProbes` and records `structuralCoverage` on each index
871
+ unit. Candidate evidence must cover every probe applicable to the selected
872
+ output profile. Coverage is based on source-backed evidence paths, not Markdown
873
+ page count, so one aggregate page can pass while an entry-only static module
874
+ card cannot.
875
+
736
876
  The extractor returns knowledge semantics (`nodeRef`, rendered Markdown,
737
877
  Review summary and source-backed evidence). It must not write `knowledge/`,
738
878
  `.tmp/context-runtime/lifecycle/candidates.jsonl`, extraction fingerprints or
@@ -772,7 +912,9 @@ Available structural libraries include:
772
912
  The packages return syntax and repository facts only. They do not classify
773
913
  product meaning, choose candidate identities, or write lifecycle state. The
774
914
  knowledge project owns that mapping. Context CLI does not auto-install these
775
- packages and does not expose a built-in Go or Rush phase.
915
+ packages and does not expose a built-in Go or Rush phase. Detection does not
916
+ execute or replace an optional parser; it makes the matching parser contract
917
+ and its evidence coverage auditable before candidate writes.
776
918
 
777
919
  ### `reviewValidity`
778
920
 
@@ -231,9 +231,10 @@ The default KB template uses the variables above to generate a starter index:
231
231
  - links to generated directory indexes when a directory exceeds the configured
232
232
  inline-entry threshold.
233
233
 
234
- `context build` always provides selected OKF root indexes. By default, a
235
- non-root directory gets its own index only when it contains more than 50
236
- descendant knowledge pages. Configure this with
234
+ `context build` always provides selected OKF root indexes. By default,
235
+ directory indexes are planned bottom-up, and a non-root directory gets its own
236
+ index only when it would expose more than 50 page or retained child-index
237
+ entries to its parent. Configure this with
237
238
  `kbPackage({ navigation: { foldDirectoryIndexes, maxInlineEntries } })`.
238
239
 
239
240
  The output is only a starter. Edit the files under
package/index.d.ts CHANGED
@@ -5,8 +5,8 @@ export type { CodegraphCollection, DocumentMainlineCollection, EntityStatus, Kno
5
5
  export { assertDocumentMainlineCollection, assertKnowledgeCollection, assertMainlineCollection, assertOkfRoot, assertTopLevelNamespace, DOC_MAINLINE_COLLECTIONS, DEFAULT_PACKAGE_NAVIGATION, KNOWLEDGE_COLLECTIONS, MAINLINE_COLLECTIONS, OKF_ROOTS, TOP_LEVEL_NAMESPACES, } from "./contracts.js";
6
6
  export { assertDocumentEvidenceSectionMetadata, DOCUMENT_COMPILE_ACTION_SCHEMA_VERSION, DOCUMENT_EVIDENCE_SECTION_VALIDATION_STAGES, DOCUMENT_SECTION_CONTENT_MODES, DOCUMENT_STRUCTURE_SCHEMA_VERSION, } from "./documentEvidence.js";
7
7
  export type { DocumentEvidenceSectionMetadata, DocumentEvidenceSectionValidationOptions, DocumentEvidenceSectionValidationStage, DocumentSectionContentMode, } from "./documentEvidence.js";
8
- export { alignProse, captureFile, captureLark, compileProse, customPhase, extractCustom, extractTs, ExtractTsConfigurationError, NO_ENTRY_DETECTED, mdxJsonDocs, reviewValidity, } from "./phases.js";
9
- export type { AlignProsePhaseDefinition, CaptureFilePhaseDefinition, CaptureLarkPhaseDefinition, CompileProsePhaseDefinition, ContextPhase, ContextPhaseContext, CustomPhaseDefinition, CustomCodeCandidateDraft, CustomCodeCandidateEdge, CustomCodeCandidateReview, CustomCodeEvidence, CustomCodeExtractionContext, CustomCodeExtractionResult, CustomCodeExtractor, ExtractCustomPhaseDefinition, ExtractTsPhaseDefinition, PhaseDefinition, PhaseResourceReference, ReviewValidityPhaseDefinition, ReviewValidityScope, } from "./phases.js";
8
+ export { alignProse, captureFile, captureLark, compileProse, CODE_INDEX_CAPABILITIES, CODE_INDEX_COVERAGE_KINDS, CODE_INDEX_LIFECYCLES, CODE_INDEX_MODULE_FACETS, CODE_INDEX_MODULE_TYPES, CODE_INDEX_OUTPUT_PROFILES, customPhase, extractCustom, extractTs, ExtractTsConfigurationError, NO_ENTRY_DETECTED, requiredCodeIndexCoverage, mdxJsonDocs, reviewValidity, } from "./phases.js";
9
+ export type { AlignProsePhaseDefinition, CaptureFilePhaseDefinition, CaptureLarkPhaseDefinition, CompileProsePhaseDefinition, ContextPhase, ContextPhaseContext, CustomPhaseDefinition, CustomCodeCandidateDraft, CustomCodeCandidateEdge, CustomCodeCandidateSection, CustomCodeCandidateReview, CustomCodeEvidence, CustomCodeExtractionContext, CustomCodeExtractionResult, CustomCodeExtractor, CodeIndexCapability, CodeIndexCapabilityGap, CodeIndexCoverageKind, CodeIndexInspectionAdapter, CodeIndexInspectionContext, CodeIndexInspectionFinding, CodeIndexInspectionFindingKind, CodeIndexInspectionResult, CodeIndexLifecycle, CodeIndexModuleFacet, CodeIndexModuleType, CodeIndexOutputProfile, CodeIndexUnitPlan, ExtractCustomPhaseDefinition, ExtractTsPhaseDefinition, PhaseDefinition, PhaseResourceReference, ReviewValidityPhaseDefinition, ReviewValidityScope, } from "./phases.js";
10
10
  export { allSources, DEFAULT_FILE_SOURCES_REGISTRY_PATH, DEFAULT_LARK_SOURCES_REGISTRY_PATH, DEFAULT_REPO_SOURCES_REGISTRY_PATH, loadSourcesRegistry, resolveSourceReference, source, } from "./sources.js";
11
11
  export type { DocumentSourceDefinition, DocumentSourceReference, DocumentSourceType, FileSourceDefinition, FileSourceReference, FileSourceRegistryEntry, LarkSourceDefinition, LarkSourceReference, LarkSourceRegistryEntry, LoadSourcesRegistryOptions, ProjectSourceDefinition, RepoProjectSourceDefinition, RepoSourceDefinition, RepoSourceReference, RepoSourceRegistryEntry, RepoSourcesRegistry, SourceCollectionReference, SourceDefinition, SourceReference, SourcesRegistry, SourceType, } from "./sources.js";
12
12
  export type TemplateVarValue = string | number | boolean | null | Record<string, unknown> | readonly Record<string, unknown>[];
@@ -23,9 +23,9 @@ export type PackageDistributionDefinition = {
23
23
  knowledgeNamespace: string;
24
24
  };
25
25
  export type PackageAssetOptimizationDefinition = {
26
- /** Image codec provider resolved from the Context workspace. */
26
+ /** Image codec provider supplied by Context CLI. */
27
27
  processor: "sharp";
28
- /** Lossless is the safe default; lossy WebP must be selected explicitly. */
28
+ /** Explicit output policy. Omit optimize to use Context's adaptive package budget. */
29
29
  mode?: "lossless-webp" | "webp";
30
30
  /** WebP quality for lossy mode. */
31
31
  quality?: number;
@@ -40,7 +40,7 @@ export type PackageAssetDefinition = {
40
40
  /** Optional HTTPS raw root. Context appends knowledge/assets/**; {commit} is supported. */
41
41
  urlPrefix?: string;
42
42
  } | {
43
- /** Copy resources into the package. */
43
+ /** Copy resources into the package. This is the default delivery. */
44
44
  delivery: "bundle";
45
45
  /** Optional image optimization, resolved from the Context workspace. */
46
46
  optimize?: PackageAssetOptimizationDefinition;
package/index.js CHANGED
@@ -7019,7 +7019,95 @@ var assertDocumentEvidenceSectionMetadata = (section, options, field = "document
7019
7019
  }
7020
7020
  assertHasSourceRefs(section, field);
7021
7021
  };
7022
- // src/phases.ts
7022
+ // src/codeIndexPlan.ts
7023
+ var CODE_INDEX_MODULE_TYPES = [
7024
+ "api-service",
7025
+ "service",
7026
+ "background-runtime",
7027
+ "sdk-library",
7028
+ "web-application",
7029
+ "adapter",
7030
+ "cli-tool",
7031
+ "monorepo-container",
7032
+ "contract-source",
7033
+ "derived-source",
7034
+ "unknown"
7035
+ ];
7036
+ var CODE_INDEX_MODULE_FACETS = [
7037
+ "page-routing",
7038
+ "public-api",
7039
+ "protocol-provider",
7040
+ "protocol-consumer",
7041
+ "event-producer",
7042
+ "event-consumer",
7043
+ "persistence",
7044
+ "plugin-extension",
7045
+ "configuration-runtime",
7046
+ "build-release",
7047
+ "cross-module-chain",
7048
+ "generated-contract"
7049
+ ];
7050
+ var CODE_INDEX_OUTPUT_PROFILES = [
7051
+ "protocol-index",
7052
+ "service-boundary",
7053
+ "runtime-map",
7054
+ "public-api-reference",
7055
+ "module-map",
7056
+ "application-map",
7057
+ "adapter-contract",
7058
+ "command-map",
7059
+ "module-registry",
7060
+ "cross-module-flow",
7061
+ "provenance-only"
7062
+ ];
7063
+ var CODE_INDEX_COVERAGE_KINDS = [
7064
+ "responsibility",
7065
+ "entrypoint",
7066
+ "operation",
7067
+ "contract",
7068
+ "handoff",
7069
+ "state-boundary",
7070
+ "failure-recovery",
7071
+ "delivery",
7072
+ "source-authority"
7073
+ ];
7074
+ var PROFILE_COVERAGE = {
7075
+ "module-map": ["responsibility", "entrypoint"],
7076
+ "application-map": ["entrypoint", "operation", "handoff"],
7077
+ "protocol-index": ["contract", "operation", "handoff"],
7078
+ "service-boundary": ["operation", "handoff"],
7079
+ "runtime-map": ["entrypoint", "operation", "failure-recovery"],
7080
+ "public-api-reference": ["contract"],
7081
+ "adapter-contract": ["contract", "handoff"],
7082
+ "command-map": ["entrypoint", "operation", "failure-recovery"],
7083
+ "module-registry": ["responsibility", "source-authority"],
7084
+ "cross-module-flow": ["operation", "handoff"],
7085
+ "provenance-only": ["source-authority"]
7086
+ };
7087
+ function requiredCodeIndexCoverage(input) {
7088
+ const required = new Set(PROFILE_COVERAGE[input.outputProfile]);
7089
+ for (const facet of input.facets ?? []) {
7090
+ if (facet === "build-release")
7091
+ required.add("delivery");
7092
+ if (facet === "persistence" || facet === "configuration-runtime")
7093
+ required.add("state-boundary");
7094
+ if (facet === "generated-contract")
7095
+ required.add("source-authority");
7096
+ }
7097
+ return [...required];
7098
+ }
7099
+ var CODE_INDEX_CAPABILITIES = [
7100
+ "complete",
7101
+ "project-adapter",
7102
+ "material-required"
7103
+ ];
7104
+ var CODE_INDEX_LIFECYCLES = [
7105
+ "authoritative",
7106
+ "generated",
7107
+ "mirrored",
7108
+ "legacy",
7109
+ "vendored"
7110
+ ];
7023
7111
  var NO_ENTRY_DETECTED = "NO_ENTRY_DETECTED";
7024
7112
 
7025
7113
  class ExtractTsConfigurationError extends TypeError {
@@ -7037,6 +7125,68 @@ function normalizeExtractEntry(value) {
7037
7125
  }
7038
7126
  return segments.join("/");
7039
7127
  }
7128
+ function requiredIndexText(value, field) {
7129
+ const normalized = value.trim();
7130
+ if (normalized.length === 0)
7131
+ throw new TypeError(`${field} must be a non-empty string`);
7132
+ return normalized;
7133
+ }
7134
+ function requiredIndexEnum(value, field, allowed) {
7135
+ if (typeof value !== "string" || !allowed.includes(value)) {
7136
+ throw new TypeError(`${field} must be one of: ${allowed.join(", ")}`);
7137
+ }
7138
+ return value;
7139
+ }
7140
+ function normalizeIndexUnit(unit, field) {
7141
+ const inputSources = [...new Set(unit.inputSources.map((source, index) => requiredIndexText(source, `${field}.inputSources[${index}]`)))];
7142
+ if (inputSources.length === 0) {
7143
+ throw new TypeError(`${field}.inputSources must contain at least one registered source name`);
7144
+ }
7145
+ const moduleType = requiredIndexEnum(unit.moduleType, `${field}.moduleType`, CODE_INDEX_MODULE_TYPES);
7146
+ const moduleTypes = [...new Set([moduleType, ...(unit.moduleTypes ?? []).map((value, index) => requiredIndexEnum(value, `${field}.moduleTypes[${index}]`, CODE_INDEX_MODULE_TYPES))])];
7147
+ if (moduleTypes.includes("unknown") && moduleTypes.length > 1) {
7148
+ throw new TypeError(`${field}.moduleTypes cannot combine unknown with a known module type`);
7149
+ }
7150
+ const facets = [...new Set((unit.facets ?? []).map((value, index) => requiredIndexEnum(value, `${field}.facets[${index}]`, CODE_INDEX_MODULE_FACETS)))];
7151
+ return {
7152
+ id: requiredIndexText(unit.id, `${field}.id`),
7153
+ inputSources,
7154
+ outputOwner: requiredIndexText(unit.outputOwner, `${field}.outputOwner`),
7155
+ moduleType,
7156
+ moduleTypes,
7157
+ facets,
7158
+ ...unit.moduleTypeEvidence === undefined ? {} : {
7159
+ moduleTypeEvidence: unit.moduleTypeEvidence.map((item, index) => requiredIndexText(item, `${field}.moduleTypeEvidence[${index}]`))
7160
+ },
7161
+ outputProfile: requiredIndexEnum(unit.outputProfile, `${field}.outputProfile`, CODE_INDEX_OUTPUT_PROFILES),
7162
+ responsibility: requiredIndexText(unit.responsibility, `${field}.responsibility`),
7163
+ entries: unit.entries.map((entry, index) => requiredIndexText(entry, `${field}.entries[${index}]`)),
7164
+ pageKinds: unit.pageKinds.map((kind, index) => requiredIndexText(kind, `${field}.pageKinds[${index}]`)),
7165
+ protocols: unit.protocols.map((protocol, index) => requiredIndexText(protocol, `${field}.protocols[${index}]`)),
7166
+ dependencies: unit.dependencies.map((dependency, index) => requiredIndexText(dependency, `${field}.dependencies[${index}]`)),
7167
+ exclusions: unit.exclusions.map((exclusion, index) => requiredIndexText(exclusion, `${field}.exclusions[${index}]`)),
7168
+ ...unit.lifecycle === undefined ? {} : {
7169
+ lifecycle: requiredIndexEnum(unit.lifecycle, `${field}.lifecycle`, CODE_INDEX_LIFECYCLES)
7170
+ },
7171
+ ...unit.sourceOfTruth === undefined ? {} : { sourceOfTruth: requiredIndexText(unit.sourceOfTruth, `${field}.sourceOfTruth`) },
7172
+ capability: requiredIndexEnum(unit.capability, `${field}.capability`, CODE_INDEX_CAPABILITIES)
7173
+ };
7174
+ }
7175
+ function assertUniqueIndexUnits(units, field) {
7176
+ const ids = new Set;
7177
+ const owners = new Set;
7178
+ for (const unit of units) {
7179
+ if (ids.has(unit.id))
7180
+ throw new TypeError(`${field} contains duplicate id: ${unit.id}`);
7181
+ if (owners.has(unit.outputOwner)) {
7182
+ throw new TypeError(`${field} contains duplicate outputOwner: ${unit.outputOwner}`);
7183
+ }
7184
+ ids.add(unit.id);
7185
+ owners.add(unit.outputOwner);
7186
+ }
7187
+ }
7188
+
7189
+ // src/phases.ts
7040
7190
  var getSourceType = (sourceDefinition) => {
7041
7191
  if (sourceDefinition.kind === "source.collection" || sourceDefinition.kind === "source.ref") {
7042
7192
  if (!("type" in sourceDefinition)) {
@@ -7239,6 +7389,26 @@ var extractTs = (definition) => {
7239
7389
  throw new ExtractTsConfigurationError("extractTs entries must contain at least one source-relative file path");
7240
7390
  }
7241
7391
  const entries = definition.entries === undefined ? undefined : [...new Set(definition.entries.map(normalizeExtractEntry))];
7392
+ const defaultSourceName = sourceDefinition.kind === "source.collection" ? sourceDefinition.type : sourceDefinition.name;
7393
+ const exportedOnly = definition.exportedOnly ?? mode === "exports";
7394
+ const indexUnits = (definition.indexUnits ?? (sourceDefinition.kind === "source.collection" ? [] : [{
7395
+ id: defaultSourceName,
7396
+ inputSources: [defaultSourceName],
7397
+ outputOwner: defaultSourceName,
7398
+ moduleType: mode === "exports" ? "sdk-library" : "unknown",
7399
+ moduleTypes: [mode === "exports" ? "sdk-library" : "unknown"],
7400
+ facets: mode === "exports" ? ["public-api"] : [],
7401
+ moduleTypeEvidence: mode === "exports" ? ["Package export entries selected by extractTs exports mode."] : [],
7402
+ outputProfile: mode === "exports" ? "public-api-reference" : "module-map",
7403
+ responsibility: mode === "exports" ? "Index the stable exported contracts of this module." : "Index the configured structural scope of this module.",
7404
+ entries: entries ?? [],
7405
+ pageKinds: mode === "exports" ? ["public-contract"] : ["module-map"],
7406
+ protocols: [],
7407
+ dependencies: [],
7408
+ exclusions: [],
7409
+ capability: "complete"
7410
+ }])).map((unit, index) => normalizeIndexUnit(unit, `extractTs indexUnits[${index}]`));
7411
+ assertUniqueIndexUnits(indexUnits, "extractTs indexUnits");
7242
7412
  const phase = {
7243
7413
  kind: "phase.extract.ts",
7244
7414
  id: `extract:${sourceId}:${definition.collection}`,
@@ -7257,7 +7427,9 @@ var extractTs = (definition) => {
7257
7427
  include: definition.include ?? ["src/**/*.{ts,tsx}"],
7258
7428
  mode,
7259
7429
  ...entries !== undefined ? { entries } : {},
7260
- exportedOnly: definition.exportedOnly ?? mode === "exports",
7430
+ exportedOnly,
7431
+ indexPlan: definition.indexUnits !== undefined || sourceDefinition.kind !== "source.collection" && mode === "exports" && exportedOnly ? "declared" : "inferred",
7432
+ indexUnits,
7261
7433
  out: {
7262
7434
  kind: "codegraph-entities",
7263
7435
  candidateFile: ".tmp/context-runtime/lifecycle/candidates.jsonl",
@@ -7280,6 +7452,8 @@ var extractCustom = (definition) => {
7280
7452
  throw new TypeError(`extractCustom collection must be codegraph: ${definition.collection}`);
7281
7453
  }
7282
7454
  const sources = definition.sources.map((sourceDefinition) => bindSourceType(sourceDefinition, "repo", "extractCustom source"));
7455
+ const indexUnits = (definition.indexUnits ?? []).map((unit, index) => normalizeIndexUnit(unit, `extractCustom indexUnits[${index}]`));
7456
+ assertUniqueIndexUnits(indexUnits, "extractCustom indexUnits");
7283
7457
  return {
7284
7458
  kind: "phase.extract.custom",
7285
7459
  id,
@@ -7292,6 +7466,9 @@ var extractCustom = (definition) => {
7292
7466
  }],
7293
7467
  sources,
7294
7468
  collection: definition.collection,
7469
+ indexPlan: definition.indexUnits === undefined ? "inferred" : "declared",
7470
+ indexUnits,
7471
+ ...definition.inspect === undefined ? {} : { inspect: definition.inspect },
7295
7472
  extract: definition.extract
7296
7473
  };
7297
7474
  };
@@ -11940,7 +12117,7 @@ var normalizePackageAssetOptimization = (assets) => {
11940
12117
  };
11941
12118
  var normalizePackageAssets = (assets) => {
11942
12119
  if (assets === undefined)
11943
- return;
12120
+ return { delivery: "bundle" };
11944
12121
  if (assets.delivery === "bundle") {
11945
12122
  return {
11946
12123
  delivery: "bundle",
@@ -12017,7 +12194,7 @@ var kbPackage = (definition) => {
12017
12194
  ...base,
12018
12195
  navigation: normalizePackageNavigation(definition.navigation),
12019
12196
  ...distribution === undefined ? {} : { distribution },
12020
- ...assets === undefined ? {} : { assets }
12197
+ assets
12021
12198
  };
12022
12199
  };
12023
12200
  var llmsPackage = (definition) => ({
@@ -12028,6 +12205,7 @@ export {
12028
12205
  source,
12029
12206
  reviewValidity,
12030
12207
  resolveSourceReference,
12208
+ requiredCodeIndexCoverage,
12031
12209
  mdxJsonDocs,
12032
12210
  loadSourcesRegistry,
12033
12211
  llmsPackage,
@@ -12061,5 +12239,11 @@ export {
12061
12239
  DEFAULT_REPO_SOURCES_REGISTRY_PATH,
12062
12240
  DEFAULT_PACKAGE_NAVIGATION,
12063
12241
  DEFAULT_LARK_SOURCES_REGISTRY_PATH,
12064
- DEFAULT_FILE_SOURCES_REGISTRY_PATH
12242
+ DEFAULT_FILE_SOURCES_REGISTRY_PATH,
12243
+ CODE_INDEX_OUTPUT_PROFILES,
12244
+ CODE_INDEX_MODULE_TYPES,
12245
+ CODE_INDEX_MODULE_FACETS,
12246
+ CODE_INDEX_LIFECYCLES,
12247
+ CODE_INDEX_COVERAGE_KINDS,
12248
+ CODE_INDEX_CAPABILITIES
12065
12249
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/context",
3
- "version": "0.6.11",
3
+ "version": "0.6.16",
4
4
  "type": "module",
5
5
  "description": "Declarative SDK for Context knowledge sources, workflows, review, and package outputs",
6
6
  "license": "MIT",
package/phases.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { type FileCaptureProcessorDefinition, type KnowledgeCollection, type MarkdownTransform, type PackageKind, type PackageSelectDefinition, type DocumentMainlineCollection } from "./contracts.js";
2
2
  import { DOCUMENT_COMPILE_ACTION_SCHEMA_VERSION } from "./documentEvidence.js";
3
+ import { type CodeIndexCoverageKind, type CodeIndexInspectionAdapter, type CodeIndexUnitPlan } from "./codeIndexPlan.js";
4
+ export { CODE_INDEX_CAPABILITIES, CODE_INDEX_COVERAGE_KINDS, CODE_INDEX_LIFECYCLES, CODE_INDEX_MODULE_FACETS, CODE_INDEX_MODULE_TYPES, CODE_INDEX_OUTPUT_PROFILES, ExtractTsConfigurationError, NO_ENTRY_DETECTED, requiredCodeIndexCoverage, } from "./codeIndexPlan.js";
5
+ export type { CodeIndexCapability, CodeIndexCapabilityGap, CodeIndexCoverageKind, CodeIndexInspectionAdapter, CodeIndexInspectionContext, CodeIndexInspectionFinding, CodeIndexInspectionFindingKind, CodeIndexInspectionResult, CodeIndexLifecycle, CodeIndexModuleFacet, CodeIndexModuleType, CodeIndexOutputProfile, CodeIndexUnitPlan, } from "./codeIndexPlan.js";
3
6
  import type { DocumentSourceDefinition, DocumentSourceType, FileSourceDefinition, FileSourceReference, LarkSourceDefinition, LarkSourceReference, RepoProjectSourceDefinition, SourceCollectionReference, SourceDefinition } from "./sources.js";
4
7
  export type PhaseResourceReference = {
5
8
  kind: "source";
@@ -66,6 +69,8 @@ export type ExtractTsPhaseDefinition = {
66
69
  mode: "exports" | "scan";
67
70
  entries?: readonly string[];
68
71
  exportedOnly: boolean;
72
+ indexPlan: "declared" | "inferred";
73
+ indexUnits: readonly CodeIndexUnitPlan[];
69
74
  transform?: MarkdownTransform | readonly MarkdownTransform[];
70
75
  out: {
71
76
  kind: "codegraph-entities";
@@ -97,22 +102,37 @@ export interface CustomCodeCandidateEdge {
97
102
  relationType: string;
98
103
  evidence: readonly CustomCodeEvidence[];
99
104
  }
105
+ export interface CustomCodeCandidateSection {
106
+ id: string;
107
+ kind: CodeIndexCoverageKind;
108
+ title: string;
109
+ markdown: string;
110
+ evidence: readonly CustomCodeEvidence[];
111
+ }
100
112
  export interface CustomCodeCandidateDraft {
101
113
  nodeRef: string;
102
114
  kind: string;
103
115
  visibility: string;
104
116
  module: string;
105
- markdown: string;
117
+ markdown?: string;
118
+ /** Evidence-scoped aggregate sections used to prove output-profile coverage. */
119
+ sections?: readonly CustomCodeCandidateSection[];
106
120
  evidence: readonly CustomCodeEvidence[];
107
121
  review: CustomCodeCandidateReview;
108
122
  edges?: readonly CustomCodeCandidateEdge[];
109
123
  }
110
124
  export interface CustomCodeExtractionResult {
111
- candidates: readonly CustomCodeCandidateDraft[];
125
+ candidates: readonly CustomCodeCandidateDraft[] | Iterable<CustomCodeCandidateDraft> | AsyncIterable<CustomCodeCandidateDraft>;
112
126
  }
113
127
  export interface CustomCodeExtractionContext {
114
128
  projectRoot: string;
115
129
  runId: string;
130
+ /** CLI-resolved source roots. Use these paths instead of environment-specific checkout paths. */
131
+ sources: readonly {
132
+ name: string;
133
+ materializedAt: string;
134
+ absolutePath: string;
135
+ }[];
116
136
  }
117
137
  export type CustomCodeExtractor = (context: CustomCodeExtractionContext) => CustomCodeExtractionResult | Promise<CustomCodeExtractionResult>;
118
138
  export type ExtractCustomPhaseDefinition = {
@@ -122,13 +142,11 @@ export type ExtractCustomPhaseDefinition = {
122
142
  writes: readonly PhaseResourceReference[];
123
143
  sources: readonly RepoProjectSourceDefinition[];
124
144
  collection: "codegraph";
145
+ indexPlan: "declared" | "inferred";
146
+ indexUnits: readonly CodeIndexUnitPlan[];
147
+ inspect?: CodeIndexInspectionAdapter;
125
148
  extract: CustomCodeExtractor;
126
149
  };
127
- export declare const NO_ENTRY_DETECTED: "NO_ENTRY_DETECTED";
128
- export declare class ExtractTsConfigurationError extends TypeError {
129
- readonly code: "NO_ENTRY_DETECTED";
130
- constructor(message: string);
131
- }
132
150
  export type CaptureFilePhaseDefinition = {
133
151
  kind: "phase.capture.file";
134
152
  id: string;
@@ -226,12 +244,15 @@ export declare const extractTs: (definition: {
226
244
  mode?: "exports" | "scan";
227
245
  entries?: readonly string[];
228
246
  exportedOnly?: boolean;
247
+ indexUnits?: readonly CodeIndexUnitPlan[];
229
248
  transform?: MarkdownTransform | readonly MarkdownTransform[];
230
249
  }) => ExtractTsPhaseDefinition;
231
250
  export declare const extractCustom: (definition: {
232
251
  id: string;
233
252
  sources: readonly RepoProjectSourceDefinition[];
234
253
  collection: "codegraph";
254
+ indexUnits?: readonly CodeIndexUnitPlan[];
255
+ inspect?: CodeIndexInspectionAdapter;
235
256
  extract: CustomCodeExtractor;
236
257
  }) => ExtractCustomPhaseDefinition;
237
258
  export declare const reviewValidity: (definition: {