@c4a/context-cli 0.5.41-beta.4 → 0.5.41-beta.5

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/cli.js CHANGED
@@ -38084,6 +38084,7 @@ var init_alignWorkflowStructureSchemas = __esm(() => {
38084
38084
  "Skipping a relation-only, navigation-only, or placeholder-only source does not dispose of its coverable blocks. If you do not emit any Node for that source, still add an ownership_groups[] source_id rule that marks the source context_only or ignored; placeholder-only sources with no kept Node usually use ignored.",
38085
38085
  "Emit depends_on only when cited blocks explicitly say one Node consumes, requires, calls, is configured by, or is downstream of another Node as a prerequisite, capability provider, upstream input, runtime dependency, or data-flow source. Direction is consumer/downstream -> provider/upstream.",
38086
38086
  "Do not emit depends_on for parent/child containment, Related/See also lists, sibling co-occurrence, shared table membership, name similarity, or a plain mention without a dependency predicate.",
38087
+ "Use edges[].from/to for endpoints, or from_ref/to_ref for llm_slug_hint values. Do not use source/target or consumer/provider.",
38087
38088
  "edges[].evidence_blocks must include the block that states the dependency. If the relation matters but evidence is missing, leave the edge out or add an unresolved question instead of guessing.",
38088
38089
  "The persisted workspace truth remains canonical align-structure-decision; this schema is an authoring input only."
38089
38090
  ]
@@ -45537,7 +45538,7 @@ function nodeLanguageIssues(input) {
45537
45538
  return [{
45538
45539
  severity: "warning",
45539
45540
  code: NODE_GENERATION_LANGUAGE_INCONSISTENT,
45540
- message: `node ${fields.join("/")} appears to be English prose, but workspace generation language is Chinese`,
45541
+ message: `generated node metadata ${fields.join("/")} appears to be English prose, but workspace generation language is Chinese`,
45541
45542
  path: input.path,
45542
45543
  slug: input.node.id
45543
45544
  }];
@@ -46954,13 +46955,15 @@ function staleSourceSnapshot(input) {
46954
46955
  };
46955
46956
  }
46956
46957
  function pushStaleSourceSnapshotIssue(input) {
46958
+ const sourceRef = input.sourceRef ?? input.section.source_ref;
46957
46959
  input.issues.push({
46958
46960
  severity: "error",
46959
46961
  code: "stale-node-source-snapshot",
46960
- message: `section source_ref "${input.section.source_ref}" cannot resolve because node.sources[] points to stale snapshot "${input.sourceEntry}" (latest @${input.stale.latest_snapshot_hash})`,
46962
+ message: `section source_ref "${sourceRef}" cannot resolve because node.sources[] points to stale snapshot "${input.sourceEntry}" (latest @${input.stale.latest_snapshot_hash})`,
46961
46963
  path: input.node.relativePath,
46962
46964
  slug: input.node.parsed.node.id,
46963
- sectionId: input.section.id
46965
+ sectionId: input.section.id,
46966
+ source_ref: sourceRef
46964
46967
  });
46965
46968
  }
46966
46969
  function normalizedRawForCompare(value) {
@@ -47000,12 +47003,122 @@ function pushSectionRawStateIssues(input) {
47000
47003
  });
47001
47004
  }
47002
47005
  }
47006
+ function pushRawSourceRefIssue(input) {
47007
+ input.issues.push({
47008
+ severity: "error",
47009
+ code: input.code,
47010
+ message: input.message,
47011
+ path: input.node.relativePath,
47012
+ slug: input.node.parsed.node.id,
47013
+ sectionId: input.section.id,
47014
+ source_ref: input.sourceRef
47015
+ });
47016
+ }
47017
+ async function validateRawSectionSourceRef(input) {
47018
+ const { ctxDir, node: node3, section, sourceRef, sourcesById, rawBlockCache, issues } = input;
47019
+ const parsed = parseSourceRef(sourceRef);
47020
+ if (!parsed) {
47021
+ pushRawSourceRefIssue({
47022
+ issues,
47023
+ node: node3,
47024
+ section,
47025
+ sourceRef,
47026
+ code: "invalid-source-ref",
47027
+ message: `section source_ref "${sourceRef}" must use ${SOURCE_REF_FORMAT_HINT}, for example ${SOURCE_REF_FORMAT_EXAMPLE}`
47028
+ });
47029
+ return null;
47030
+ }
47031
+ const aliasMatch = /^src-(\d+)#/.exec(sourceRef);
47032
+ const aliasIndex2 = Number(aliasMatch?.[1] ?? "0");
47033
+ if (aliasIndex2 <= 0 || aliasIndex2 > node3.parsed.node.sources.length) {
47034
+ pushRawSourceRefIssue({
47035
+ issues,
47036
+ node: node3,
47037
+ section,
47038
+ sourceRef,
47039
+ code: "dangling-source-alias",
47040
+ message: `section source_ref "${sourceRef}" points past node.sources[]`
47041
+ });
47042
+ return null;
47043
+ }
47044
+ const sourceEntry = node3.parsed.node.sources[aliasIndex2 - 1];
47045
+ if (!sourceEntry) {
47046
+ pushRawSourceRefIssue({
47047
+ issues,
47048
+ node: node3,
47049
+ section,
47050
+ sourceRef,
47051
+ code: "dangling-source-alias",
47052
+ message: `section source_ref "${sourceRef}" points to an empty node.sources[] slot`
47053
+ });
47054
+ return null;
47055
+ }
47056
+ const sourceId = sourceIdWithoutVersion(sourceEntry);
47057
+ const source2 = sourcesById.get(sourceId);
47058
+ if (source2?.status !== "active")
47059
+ return null;
47060
+ const stale = staleSourceSnapshot({ sourceEntry, source: source2 });
47061
+ if (stale !== null) {
47062
+ pushStaleSourceSnapshotIssue({ issues, node: node3, section, sourceEntry, sourceRef, stale });
47063
+ return null;
47064
+ }
47065
+ const resolvedSourceRef = await resolveHashedSourceRef({
47066
+ ctxDir,
47067
+ sourceRef,
47068
+ nodeSources: node3.parsed.node.sources,
47069
+ allowStableLocatorReanchor: true
47070
+ });
47071
+ if (resolvedSourceRef === null) {
47072
+ pushRawSourceRefIssue({
47073
+ issues,
47074
+ node: node3,
47075
+ section,
47076
+ sourceRef,
47077
+ code: "invalid-source-ref",
47078
+ message: `section source_ref "${sourceRef}" must include a valid @hash for a raw evidence block`
47079
+ });
47080
+ return null;
47081
+ }
47082
+ if (input.validateCanonical !== false && resolvedSourceRef.source_ref !== sourceRef) {
47083
+ pushRawSourceRefIssue({
47084
+ issues,
47085
+ node: node3,
47086
+ section,
47087
+ sourceRef,
47088
+ code: "invalid-source-ref",
47089
+ message: `section source_ref "${sourceRef}" is not canonical; expected "${resolvedSourceRef.source_ref}"`
47090
+ });
47091
+ return null;
47092
+ }
47093
+ if (input.validateRawOverlap !== false) {
47094
+ const hasOverlap = await sourceRefHasRawBlockOverlap({
47095
+ ctxDir,
47096
+ source: source2,
47097
+ cache: rawBlockCache,
47098
+ lineStart: parsed.lineStart,
47099
+ lineEnd: parsed.lineEnd,
47100
+ sourceEntry
47101
+ });
47102
+ if (hasOverlap === false) {
47103
+ pushRawSourceRefIssue({
47104
+ issues,
47105
+ node: node3,
47106
+ section,
47107
+ sourceRef,
47108
+ code: "source-ref-out-of-range",
47109
+ message: `section source_ref "${sourceRef}" does not overlap any raw block in latest source snapshot`
47110
+ });
47111
+ return null;
47112
+ }
47113
+ }
47114
+ return resolvedSourceRef;
47115
+ }
47003
47116
  async function pushSectionReferenceIssues(input) {
47004
47117
  const { node: node3, section, issues } = input;
47005
47118
  if (section.status === SectionStatus.deprecated)
47006
47119
  return;
47007
47120
  pushSectionRawStateIssues({ issues, node: node3, section });
47008
- const nodeHasCodeSource = node3.parsed.node.sources.some((source3) => sourceIdWithoutVersion(source3).startsWith("aspect:code:"));
47121
+ const nodeHasCodeSource = node3.parsed.node.sources.some((source2) => sourceIdWithoutVersion(source2).startsWith("aspect:code:"));
47009
47122
  for (const target of section.refers_to_nodes ?? []) {
47010
47123
  if (!input.knownRefSlugs.has(target)) {
47011
47124
  issues.push({
@@ -47027,6 +47140,21 @@ async function pushSectionReferenceIssues(input) {
47027
47140
  });
47028
47141
  }
47029
47142
  }
47143
+ for (const sourceRef of [...new Set(section.source_refs ?? [])]) {
47144
+ if (sourceRef === section.source_ref)
47145
+ continue;
47146
+ await validateRawSectionSourceRef({
47147
+ ctxDir: input.ctxDir,
47148
+ node: node3,
47149
+ section,
47150
+ sourceRef,
47151
+ sourcesById: input.sourcesById,
47152
+ rawBlockCache: input.rawBlockCache,
47153
+ issues,
47154
+ validateRawOverlap: false,
47155
+ validateCanonical: false
47156
+ });
47157
+ }
47030
47158
  if (section.source_ref.length === 0)
47031
47159
  return;
47032
47160
  const dispatch = dispatchSourceRef(node3.parsed.node.sources, section.source_ref);
@@ -47065,13 +47193,13 @@ async function pushSectionReferenceIssues(input) {
47065
47193
  });
47066
47194
  return;
47067
47195
  }
47068
- const source3 = input.sourcesById.get(sourceIdWithoutVersion(dispatch.sourceEntry));
47196
+ const source2 = input.sourcesById.get(sourceIdWithoutVersion(dispatch.sourceEntry));
47069
47197
  await pushCodeSourceReferenceIssues({
47070
47198
  ctxDir: input.ctxDir,
47071
47199
  node: node3,
47072
47200
  section,
47073
47201
  sourceEntry: dispatch.sourceEntry,
47074
- source: source3,
47202
+ source: source2,
47075
47203
  parsed: dispatch.code,
47076
47204
  issues
47077
47205
  });
@@ -47089,11 +47217,11 @@ async function pushSectionReferenceIssues(input) {
47089
47217
  });
47090
47218
  return;
47091
47219
  }
47092
- const source3 = input.sourcesById.get(sourceIdWithoutVersion(dispatch.sourceEntry));
47093
- if (source3 !== undefined) {
47094
- const stale2 = staleSourceSnapshot({ sourceEntry: dispatch.sourceEntry, source: source3 });
47095
- if (stale2 !== null) {
47096
- pushStaleSourceSnapshotIssue({ issues, node: node3, section, sourceEntry: dispatch.sourceEntry, stale: stale2 });
47220
+ const source2 = input.sourcesById.get(sourceIdWithoutVersion(dispatch.sourceEntry));
47221
+ if (source2 !== undefined) {
47222
+ const stale = staleSourceSnapshot({ sourceEntry: dispatch.sourceEntry, source: source2 });
47223
+ if (stale !== null) {
47224
+ pushStaleSourceSnapshotIssue({ issues, node: node3, section, sourceEntry: dispatch.sourceEntry, stale });
47097
47225
  return;
47098
47226
  }
47099
47227
  }
@@ -47102,103 +47230,22 @@ async function pushSectionReferenceIssues(input) {
47102
47230
  node: node3,
47103
47231
  section,
47104
47232
  sourceEntry: dispatch.sourceEntry,
47105
- source: source3,
47233
+ source: source2,
47106
47234
  parsed: dispatch.aspectFile,
47107
47235
  issues
47108
47236
  });
47109
47237
  return;
47110
47238
  }
47111
- const parsed = parseSourceRef(section.source_ref);
47112
- if (!parsed) {
47113
- issues.push({
47114
- severity: "error",
47115
- code: "invalid-source-ref",
47116
- message: `section source_ref "${section.source_ref}" must use ${SOURCE_REF_FORMAT_HINT}, for example ${SOURCE_REF_FORMAT_EXAMPLE}`,
47117
- path: node3.relativePath,
47118
- slug: node3.parsed.node.id,
47119
- sectionId: section.id
47120
- });
47121
- return;
47122
- }
47123
- const aliasMatch = /^src-(\d+)#/.exec(section.source_ref);
47124
- const aliasIndex2 = Number(aliasMatch?.[1] ?? "0");
47125
- if (aliasIndex2 <= 0 || aliasIndex2 > node3.parsed.node.sources.length) {
47126
- issues.push({
47127
- severity: "error",
47128
- code: "dangling-source-alias",
47129
- message: `section source_ref "${section.source_ref}" points past node.sources[]`,
47130
- path: node3.relativePath,
47131
- slug: node3.parsed.node.id,
47132
- sectionId: section.id
47133
- });
47134
- return;
47135
- }
47136
- const sourceEntry = node3.parsed.node.sources[aliasIndex2 - 1];
47137
- if (!sourceEntry) {
47138
- issues.push({
47139
- severity: "error",
47140
- code: "dangling-source-alias",
47141
- message: `section source_ref "${section.source_ref}" points to an empty node.sources[] slot`,
47142
- path: node3.relativePath,
47143
- slug: node3.parsed.node.id,
47144
- sectionId: section.id
47145
- });
47146
- return;
47147
- }
47148
- const sourceId = sourceIdWithoutVersion(sourceEntry);
47149
- const source2 = input.sourcesById.get(sourceId);
47150
- if (source2?.status !== "active")
47151
- return;
47152
- const stale = staleSourceSnapshot({ sourceEntry, source: source2 });
47153
- if (stale !== null) {
47154
- pushStaleSourceSnapshotIssue({ issues, node: node3, section, sourceEntry, stale });
47155
- return;
47156
- }
47157
- const resolvedSourceRef = await resolveHashedSourceRef({
47239
+ const resolvedSourceRef = await validateRawSectionSourceRef({
47158
47240
  ctxDir: input.ctxDir,
47241
+ node: node3,
47242
+ section,
47159
47243
  sourceRef: section.source_ref,
47160
- nodeSources: node3.parsed.node.sources,
47161
- allowStableLocatorReanchor: true
47244
+ sourcesById: input.sourcesById,
47245
+ rawBlockCache: input.rawBlockCache,
47246
+ issues
47162
47247
  });
47163
47248
  if (resolvedSourceRef === null) {
47164
- issues.push({
47165
- severity: "error",
47166
- code: "invalid-source-ref",
47167
- message: `section source_ref "${section.source_ref}" must include a valid @hash for a raw evidence block`,
47168
- path: node3.relativePath,
47169
- slug: node3.parsed.node.id,
47170
- sectionId: section.id
47171
- });
47172
- return;
47173
- }
47174
- if (resolvedSourceRef.source_ref !== section.source_ref) {
47175
- issues.push({
47176
- severity: "error",
47177
- code: "invalid-source-ref",
47178
- message: `section source_ref "${section.source_ref}" is not canonical; expected "${resolvedSourceRef.source_ref}"`,
47179
- path: node3.relativePath,
47180
- slug: node3.parsed.node.id,
47181
- sectionId: section.id
47182
- });
47183
- return;
47184
- }
47185
- const hasOverlap = await sourceRefHasRawBlockOverlap({
47186
- ctxDir: input.ctxDir,
47187
- source: source2,
47188
- cache: input.rawBlockCache,
47189
- lineStart: parsed.lineStart,
47190
- lineEnd: parsed.lineEnd,
47191
- sourceEntry
47192
- });
47193
- if (hasOverlap === false) {
47194
- issues.push({
47195
- severity: "error",
47196
- code: "source-ref-out-of-range",
47197
- message: `section source_ref "${section.source_ref}" does not overlap any raw block in latest source snapshot`,
47198
- path: node3.relativePath,
47199
- slug: node3.parsed.node.id,
47200
- sectionId: section.id
47201
- });
47202
47249
  return;
47203
47250
  }
47204
47251
  const sectionEvidence = sectionEvidenceText(section);
@@ -61258,8 +61305,8 @@ var init_agentHintRegistry = __esm(() => {
61258
61305
  {
61259
61306
  code: "align-node-generation-language-inconsistent",
61260
61307
  severity: "warning",
61261
- description: "An align payload Node title or summary appears to be generated in a different language from the workspace generation policy.",
61262
- trigger: "Align validate/finalize sees English-looking node.title or node.summary while align-segments.generation_policy.language is Chinese.",
61308
+ description: "Generated Node metadata appears to use a different language from the workspace generation policy.",
61309
+ trigger: "Align validate/finalize sees English-looking node.title or node.summary while align-segments.generation_policy.language is Chinese. Source-bound Section content can still follow the cited source language.",
61263
61310
  command: "context schema align-structure-intent --view minimal --format json"
61264
61311
  },
61265
61312
  {
@@ -62139,17 +62186,17 @@ function compileAgentEngagementSummary(input) {
62139
62186
  const writeRecords = input.records.filter((record) => record.mode === "compile" && record.action !== "duplicate_skip" && record.action !== "omit" && record.action !== "ask_user");
62140
62187
  const citedRefs = new Set(writeRecords.flatMap(refsFromRecord));
62141
62188
  const templateLikeActions = writeRecords.filter((record) => contentFromRecord(record).some(isTemplateLikeContent)).length;
62142
- const derivedPrimaryTotal = citedRefs.size + input.coverage.breakdown.primary_unresolved_total + input.coverage.breakdown.primary_covered + input.coverage.breakdown.primary_skipped_total;
62189
+ const derivedPrimaryTotal = citedRefs.size + input.coverage.breakdown.primary_unresolved_total + input.coverage.breakdown.primary_skipped_total;
62143
62190
  const primaryTotal = Math.max(input.coverage.breakdown.primary_content_refs_total, derivedPrimaryTotal);
62144
62191
  const unsupportedOrLowSupport = writeRecords.filter((record) => record.action === "remove_unsupported" || judgeSupportVerdict(record) === "weak" || judgeSupportVerdict(record) === "unsupported").length + input.coverage.breakdown.blocking_unresolved;
62145
- const severity = writeRecords.length > 0 && citedRefs.size === 0 || primaryTotal > LOW_CITATION_PRIMARY_TOTAL_THRESHOLD && citedRefs.size / primaryTotal < LOW_CITATION_RATE_THRESHOLD || writeRecords.length >= 3 && (input.evidenceViews?.expected_evidence_views_total ?? 0) >= 3 && (input.evidenceViews?.expected_evidence_views_read ?? 0) <= (input.evidenceViews?.expected_evidence_views_total ?? 0) / 3 || templateLikeActions > 0 || unsupportedOrLowSupport > 0 ? "warning" : "info";
62192
+ const severity = writeRecords.length > 0 && citedRefs.size === 0 || writeRecords.length > 0 && primaryTotal > LOW_CITATION_PRIMARY_TOTAL_THRESHOLD && citedRefs.size / primaryTotal < LOW_CITATION_RATE_THRESHOLD || writeRecords.length >= 3 && (input.evidenceViews?.expected_evidence_views_total ?? 0) >= 3 && (input.evidenceViews?.expected_evidence_views_read ?? 0) <= (input.evidenceViews?.expected_evidence_views_total ?? 0) / 3 || templateLikeActions > 0 || unsupportedOrLowSupport > 0 ? "warning" : "info";
62146
62193
  return {
62147
62194
  written_actions: writeRecords.length,
62148
62195
  primary_citable_refs_total: primaryTotal,
62149
62196
  cited_refs_count: citedRefs.size,
62150
62197
  ...input.evidenceViews?.expected_evidence_views_total !== undefined ? { expected_evidence_views_total: input.evidenceViews.expected_evidence_views_total } : {},
62151
62198
  ...input.evidenceViews?.expected_evidence_views_read !== undefined ? { expected_evidence_views_read: input.evidenceViews.expected_evidence_views_read } : {},
62152
- unexpanded_primary_refs: Math.max(0, primaryTotal - citedRefs.size),
62199
+ unexpanded_primary_refs: writeRecords.length === 0 ? 0 : Math.max(0, primaryTotal - citedRefs.size),
62153
62200
  full_text_requests: input.evidenceViews?.full_text_requests ?? 0,
62154
62201
  template_like_actions: templateLikeActions,
62155
62202
  unsupported_or_low_support_actions: unsupportedOrLowSupport,
@@ -81588,6 +81635,7 @@ async function preflightSemanticDecisions(input) {
81588
81635
  const current = findTargetSection(node3, target.section_id, decision.action);
81589
81636
  const reanchorPatch = {
81590
81637
  ...decision.proposed?.source_ref !== undefined ? { source_ref: decision.proposed.source_ref } : {},
81638
+ ...decision.proposed?.source_refs !== undefined ? { source_refs: decision.proposed.source_refs } : {},
81591
81639
  ...decision.proposed?.confidence !== undefined ? { confidence: decision.proposed.confidence } : {}
81592
81640
  };
81593
81641
  const finalSection = finalSectionFromPatch(current, reanchorPatch);
@@ -81619,7 +81667,8 @@ async function preflightSemanticDecisions(input) {
81619
81667
  ctxDir: input.ctxDir,
81620
81668
  nodeSources: node3.parsed.node.sources,
81621
81669
  section,
81622
- action: decision.action
81670
+ action: decision.action,
81671
+ sourceRefs: proposed.source_refs
81623
81672
  });
81624
81673
  }
81625
81674
  }
@@ -82452,14 +82501,21 @@ async function canonicalizeProposedForNode(input) {
82452
82501
  const [sourceRef, sourceRefs2, sections] = await Promise.all([
82453
82502
  typeof input.proposed.source_ref === "string" ? canonicalizeSourceRefForNode({ ctxDir: input.ctxDir, node: input.node, sourceRef: input.proposed.source_ref }) : Promise.resolve(undefined),
82454
82503
  input.proposed.source_refs !== undefined ? Promise.all(input.proposed.source_refs.map((ref) => canonicalizeSourceRefForNode({ ctxDir: input.ctxDir, node: input.node, sourceRef: ref }))) : Promise.resolve(undefined),
82455
- input.proposed.sections !== undefined ? Promise.all(input.proposed.sections.map(async (section) => ({
82456
- ...section,
82457
- source_ref: await canonicalizeSourceRefForNode({
82458
- ctxDir: input.ctxDir,
82459
- node: input.node,
82460
- sourceRef: section.source_ref
82461
- })
82462
- }))) : Promise.resolve(undefined)
82504
+ input.proposed.sections !== undefined ? Promise.all(input.proposed.sections.map(async (section) => {
82505
+ const [sectionSourceRef, sectionSourceRefs2] = await Promise.all([
82506
+ canonicalizeSourceRefForNode({
82507
+ ctxDir: input.ctxDir,
82508
+ node: input.node,
82509
+ sourceRef: section.source_ref
82510
+ }),
82511
+ section.source_refs !== undefined ? Promise.all(section.source_refs.map((ref) => canonicalizeSourceRefForNode({ ctxDir: input.ctxDir, node: input.node, sourceRef: ref }))) : Promise.resolve(undefined)
82512
+ ]);
82513
+ return {
82514
+ ...section,
82515
+ source_ref: sectionSourceRef,
82516
+ ...sectionSourceRefs2 !== undefined ? { source_refs: [...new Set(sectionSourceRefs2)] } : {}
82517
+ };
82518
+ })) : Promise.resolve(undefined)
82463
82519
  ]);
82464
82520
  return {
82465
82521
  ...input.proposed,
@@ -82593,6 +82649,7 @@ async function applyReanchorPrimitive(input) {
82593
82649
  sectionId: input.sectionId,
82594
82650
  patch: {
82595
82651
  source_ref: input.proposed.source_ref,
82652
+ ...input.proposed.source_refs !== undefined ? { source_refs: input.proposed.source_refs } : {},
82596
82653
  confidence: input.proposed.confidence
82597
82654
  }
82598
82655
  });
@@ -88983,7 +89040,7 @@ function projectNodeContextSourceRefsIndex(value, options = {}, baseCommand = "c
88983
89040
  selectionPolicy: {
88984
89041
  ...SOURCE_REFS_SELECTION_POLICY,
88985
89042
  id: "source-refs-index-v1",
88986
- note: "Compact block id index for compile-draft source_block_ids shorthand."
89043
+ note: "Compact block id index for compile-draft source_block_ids shorthand; multi-block actions must stay within one same-source contiguous citation-eligible run."
88987
89044
  },
88988
89045
  howToExplore: sourceRefsHowToExplore({
88989
89046
  rows,
@@ -89001,7 +89058,7 @@ function projectNodeContextSourceRefsIndex(value, options = {}, baseCommand = "c
89001
89058
  view_of: "NodeContext.raw_snippets",
89002
89059
  projection_mode: projectionMode,
89003
89060
  current_workset_scope: projectionScope,
89004
- usage: "Use items[].block_id in compile-draft actions[].source_block_ids. Open source-refs detail only when text preview or canonical source_ref strings are needed.",
89061
+ usage: "Use items[].block_id in compile-draft actions[].source_block_ids. For multi-block actions, use one same-source contiguous citation-eligible run; split around skipped citation-eligible rows or run draft-scaffold when unsure. Open source-refs detail only when text preview or canonical source_ref strings are needed.",
89005
89062
  ...isRecord37(value.incremental) ? { incremental: stripAgentInternalPathFields(value.incremental) } : {},
89006
89063
  ...Object.keys(filters).length > 0 ? { filters } : {},
89007
89064
  ...window2,
@@ -91698,9 +91755,10 @@ function normalizeEdges(value, refs, slugs) {
91698
91755
  if (raw.edge_type !== "depends_on" && raw.type !== "depends_on") {
91699
91756
  rejectLegacy(`edges[${index2}].edge_type`, `document edge type must be depends_on; allowed runtime EdgeType values are ${EDGE_TYPES.join("|")}`);
91700
91757
  }
91701
- const unsupportedEndpointFields = ["consumer", "provider"].filter((field) => Object.prototype.hasOwnProperty.call(raw, field));
91758
+ const unsupportedEndpointFields = ["consumer", "provider", "source", "target"].filter((field) => Object.prototype.hasOwnProperty.call(raw, field));
91702
91759
  if (unsupportedEndpointFields.length > 0) {
91703
- reject(`edges[${index2}]`, "edge endpoint fields consumer/provider are not supported; use from/to or from_ref/to_ref", {
91760
+ const endpointLabel = unsupportedEndpointFields.join("/");
91761
+ reject(`edges[${index2}]`, `edge endpoint fields ${endpointLabel} are not supported; use from/to or from_ref/to_ref`, {
91704
91762
  reasonCode: "edge-endpoint-fields-unsupported",
91705
91763
  currentValue: Object.fromEntries(unsupportedEndpointFields.map((field) => [field, raw[field]])),
91706
91764
  availableNodeRefs: nodeRefsForHint(refs),
@@ -91713,6 +91771,7 @@ function normalizeEdges(value, refs, slugs) {
91713
91771
  }, null, 2),
91714
91772
  repairOptions: [
91715
91773
  "Rename consumer to from and provider to to.",
91774
+ "Rename source to from and target to to.",
91716
91775
  "Use from_ref/to_ref when referring to llm_slug_hint values."
91717
91776
  ]
91718
91777
  });
@@ -93386,7 +93445,7 @@ function agentHintsForNodeLanguageWarnings(input) {
93386
93445
  message: issue2.message,
93387
93446
  path: issue2.path,
93388
93447
  target_node: issue2.slug,
93389
- next_action: "Revise node.title or node.summary to follow align-segments.generation_policy.language, then rerun context align validate. Preserve product names, code identifiers, slugs, flags, block_id tokens, and citation tokens exactly when needed.",
93448
+ next_action: "Revise generated node.title or node.summary to follow align-segments.generation_policy.language, then rerun context align validate. This warning does not require translating source-bound Section content. Preserve product names, code identifiers, slugs, flags, block_id tokens, and citation tokens exactly when needed.",
93390
93449
  command: "context schema align-structure-intent --view minimal --format json",
93391
93450
  diagnostics: {
93392
93451
  ...input.expectedLanguage !== undefined ? { expected_language: input.expectedLanguage } : {},
@@ -97656,12 +97715,10 @@ function splitActionTemplate(original, split, index2) {
97656
97715
  delete action.source_block_ids;
97657
97716
  delete action.body;
97658
97717
  delete action.raw;
97718
+ delete action.summary;
97719
+ delete action.detail;
97659
97720
  action.source_refs = split.source_refs;
97660
97721
  action.content = `<rewrite content for split ${index2 + 1}; cite only listed source_refs>`;
97661
- if (typeof action.summary === "string")
97662
- action.summary = `<optional summary for split ${index2 + 1}>`;
97663
- if (typeof action.detail === "string")
97664
- action.detail = `<optional detail for split ${index2 + 1}>`;
97665
97722
  return action;
97666
97723
  }
97667
97724
  function suggestedSplitPatchTemplate(input) {
@@ -98693,14 +98750,12 @@ function draftActionTemplate(kind, rows) {
98693
98750
  return {
98694
98751
  op: "add",
98695
98752
  kind,
98696
- summary: "<write recall summary with object, mechanism, constraint, action, or entry term>",
98697
98753
  source_block_ids: sourceBlockIds2
98698
98754
  };
98699
98755
  }
98700
98756
  return {
98701
98757
  op: "add",
98702
98758
  kind,
98703
- summary: "<write recall summary with object, mechanism, constraint, action, or entry term>",
98704
98759
  source_refs: rows.map((row) => row.source_ref)
98705
98760
  };
98706
98761
  }
@@ -99478,7 +99533,7 @@ function plannedTemplateActions(sectionGroups) {
99478
99533
  return plannedReadySectionGroups(sectionGroups).flatMap((group) => recordArray3(group.draft_action_templates));
99479
99534
  }
99480
99535
  function scaffoldUsage() {
99481
- return "Confirm each action.kind and write a recall-focused summary; omit content by default because the CLI mirrors cited raw from source_block_ids/source_refs. Add content only for intentional translation, structural rewrite, or formatting preservation; c4a:raw is written only for rewritten evidence. Keep hard citation-gap actions separate; if one action cites multiple source_block_ids/source_refs, they must form one contiguous citation-eligible run from the same source, with no intervening citation-eligible block owned by another action. Read section_group_annotations when present because heading/local-heading annotations are context for your semantic split decision. Submit with context compile cycle <node> --input - --format json, or context compile draft <node> --input - for manual review.";
99536
+ return "Confirm each action.kind; add a recall-focused summary only when it improves reader or query output. Omit content by default because the CLI mirrors cited raw from source_block_ids/source_refs. Add content only for intentional translation, structural rewrite, or formatting preservation; c4a:raw is written only for rewritten evidence. Keep hard citation-gap actions separate; if one action cites multiple source_block_ids/source_refs, they must form one contiguous citation-eligible run from the same source, with no intervening citation-eligible block owned by another action. Read section_group_annotations when present because heading/local-heading annotations are context for your semantic split decision. Submit with context compile cycle <node> --input - --format json, or context compile draft <node> --input - for manual review.";
99482
99537
  }
99483
99538
  function stringArray10(value) {
99484
99539
  return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
@@ -99516,9 +99571,10 @@ function requestFullTextCommands(action, rows) {
99516
99571
  function scaffoldAction(action, refersToNodeCandidates = []) {
99517
99572
  const out2 = {
99518
99573
  op: typeof action.op === "string" ? action.op : "add",
99519
- kind: typeof action.kind === "string" ? action.kind : "description",
99520
- summary: typeof action.summary === "string" && action.summary.length > 0 ? action.summary : "<write recall summary with object, mechanism, constraint, action, or entry term>"
99574
+ kind: typeof action.kind === "string" ? action.kind : "description"
99521
99575
  };
99576
+ if (typeof action.summary === "string" && action.summary.length > 0)
99577
+ out2.summary = action.summary;
99522
99578
  const sourceBlockIds2 = stringArray10(action.source_block_ids);
99523
99579
  const sourceRefs2 = stringArray10(action.source_refs);
99524
99580
  const refersToNodes = uniqueStrings6([
@@ -99610,7 +99666,6 @@ function sourceRefsDraftScaffold(input) {
99610
99666
  const scaffold = scaffoldAction({
99611
99667
  op: "add",
99612
99668
  kind,
99613
- summary: "<write recall summary with object, mechanism, constraint, action, or entry term>",
99614
99669
  source_block_ids: typeof row.block_id === "string" ? [row.block_id] : []
99615
99670
  }, input.refersToNodeCandidates);
99616
99671
  const commands = requestFullTextCommands(scaffold, selectedRows);
@@ -99836,7 +99891,7 @@ function sourceRefsSectionGroupHint(sectionGroups) {
99836
99891
  code: "compile-source-refs-planned-section-groups",
99837
99892
  severity: "info",
99838
99893
  message: "planned_section_groups is the primary source-refs authoring path for finalized align section plans.",
99839
- next_action: splitCount > 0 ? "Use each hard-split planned_section_groups[].draft_action_templates entry as a separate draft action because non-contiguous evidence cannot form one source_ref. heading_spans/local_headings are annotations, not split commands." : "Use planned_section_groups[].draft_action_templates as the default align section scaffold. Confirm kind and summary while keeping source_block_ids shorthand when present; heading_spans/local_headings are annotations when present, not split commands.",
99894
+ next_action: splitCount > 0 ? "Use each hard-split planned_section_groups[].draft_action_templates entry as a separate draft action because non-contiguous evidence cannot form one source_ref. heading_spans/local_headings are annotations, not split commands." : "Use planned_section_groups[].draft_action_templates as the default align section scaffold. Confirm kind; add summary only when it improves reader or query output. Keep source_block_ids shorthand when present; heading_spans/local_headings are annotations when present, not split commands.",
99840
99895
  diagnostics: {
99841
99896
  planned_section_groups: sectionGroups.length,
99842
99897
  split_planned_section_groups: splitCount,
@@ -99861,7 +99916,7 @@ function sourceRefsDenseHintWithSectionGroups(hint, sectionGroups, visibleRows)
99861
99916
  delete baseHint.next_action;
99862
99917
  const plannedSuffix = sectionGroups.length > 0 ? " planned_section_groups are available for finalized align section plans." : "";
99863
99918
  const countSuffix = fullCount !== undefined && fullCount > visibleCitationRows.length ? ` (${fullCount} total in the NodeContext).` : ".";
99864
- const nextAction = sectionGroups.length > 0 ? "Use planned_section_groups[].draft_action_templates first as the align section scaffold. Confirm kind and summary; fallback may split finer only when a planned group is missing, needs attention, or you intentionally need finer-grained Sections. Treat heading_spans/local_headings as context annotations, not required prefixes." : hint.next_action;
99919
+ const nextAction = sectionGroups.length > 0 ? "Use planned_section_groups[].draft_action_templates first as the align section scaffold. Confirm kind; add summary only when it improves reader or query output. Fallback may split finer only when a planned group is missing, needs attention, or you intentionally need finer-grained Sections. Treat heading_spans/local_headings as context annotations, not required prefixes." : hint.next_action;
99865
99920
  return {
99866
99921
  ...baseHint,
99867
99922
  message: `${visibleCitationRows.length} citation-eligible snippet(s)${sourceId !== undefined ? ` from ${sourceId}` : ""} are visible in this source-refs view${countSuffix}${plannedSuffix}`,
@@ -100035,7 +100090,7 @@ function sourceRefsNextActionEnvelope(input) {
100035
100090
  views: [
100036
100091
  {
100037
100092
  id: "source-refs-index",
100038
- purpose: "Compact source-ref index contains block ids eligible for compile draft source_block_ids.",
100093
+ purpose: "Compact source-ref index contains block ids eligible for compile draft source_block_ids; multi-block actions must stay within one same-source contiguous citation-eligible run.",
100039
100094
  evidence_role: "primary",
100040
100095
  expected: true,
100041
100096
  budget_safety: "compact",
@@ -100094,14 +100149,14 @@ function writeCompileSourceRefs(context, format, options = {}) {
100094
100149
  code: "compile-source-refs-draft-scaffold-available",
100095
100150
  severity: "info",
100096
100151
  message: "source-refs view can emit a compile-draft skeleton so agents do not manually copy source_ref strings.",
100097
- next_action: "Run the draft scaffold command, confirm action.kind, and write action.summary; omit content by default because the CLI mirrors cited raw from source_block_ids. Add content only for intentional translation, structural rewrite, or formatting preservation. Keep hard citation-gap templates separate; split heading/local-heading annotated templates only when the cited evidence is semantically separable.",
100152
+ next_action: "Run the draft scaffold command and confirm action.kind. Add action.summary only when it improves reader or query output; omit content by default because the CLI mirrors cited raw from source_block_ids. Add content only for intentional translation, structural rewrite, or formatting preservation. Keep hard citation-gap templates separate; split heading/local-heading annotated templates only when the cited evidence is semantically separable.",
100098
100153
  command: draftScaffoldCommand
100099
100154
  };
100100
100155
  const indexHint = {
100101
100156
  code: "compile-source-refs-index-ready",
100102
100157
  severity: "info",
100103
100158
  message: "source-refs-index is the compact Node-scoped citation index for source_block_ids; it intentionally omits quote previews.",
100104
- next_action: "Use items[].block_id when the indexed row is the evidence you will cite. Open the detailed source-refs view only when you need quote preview or explicit source_refs.",
100159
+ next_action: "Use items[].block_id when the indexed row is the evidence you will cite. For multi-block actions, use block_ids from one same-source contiguous citation-eligible run; split around skipped citation-eligible rows, or run draft-scaffold when unsure. Open the detailed source-refs view only when you need quote preview or explicit source_refs.",
100105
100160
  command: sourceRefsIndex,
100106
100161
  detail_view_command: sourceRefsDetail
100107
100162
  };
@@ -107125,6 +107180,7 @@ function alignStructureIntentFieldGuidance() {
107125
107180
  },
107126
107181
  edges: {
107127
107182
  edge_type: "Only depends_on is accepted; direction is consumer/downstream -> provider/upstream.",
107183
+ endpoint_fields: "Use from/to for endpoints, or from_ref/to_ref for llm_slug_hint values. Do not use source/target or consumer/provider.",
107128
107184
  evidence_blocks: "Include the block that explicitly states the dependency. Do not use edges for parent/child containment or Related/See also links."
107129
107185
  }
107130
107186
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/context-cli",
3
- "version": "0.5.41-beta.4",
3
+ "version": "0.5.41-beta.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "context": "./cli.js"
package/plugin/README.md CHANGED
@@ -107,17 +107,19 @@ The compiled knowledge base can be packaged for distribution:
107
107
 
108
108
  The numbers below come from a parallel benchmark: each model runs the full c4a flow (capture → align → compile) end-to-end over the same 10-document business corpus, with no manual intervention. Quality is scored across 7 dimensions (totaling 100) based on the resulting knowledge base; duration covers the full flow from `context init` to `compile close`.
109
109
 
110
- | Dimension | Opus 4.8 (Claude) | GPT 5.5 (Codex) | Opus 4.7 (Claude) | DeepSeek V4 Pro (Claude) |
111
- |---|---:|---:|---:|---:|
112
- | Fact coverage (25) | 23 | 23 | 22 | 18 |
113
- | Fact fidelity (25) | 24 | 23 | 19 | 18 |
114
- | Structure (15) | 14 | 13 | 8 | 7 |
115
- | URL (5) | 5 | 4 | 2 | 5 |
116
- | Source ref (10) | 9 | 9 | 7 | 6 |
117
- | Section boundary (10) | 9 | 10 | 7 | 5 |
118
- | Schema (10) | 9 | 10 | 9 | 7 |
119
- | **Total** | **93** | **92** | **74** | **66** |
120
- | Duration | 30m47s | 10m07s | 19m6s | 14m44s |
110
+ | Dimension | GPT 5.5 xh fast | GPT 5.5 m fast | Opus 4.8 | Sonnet 4.6 | DeepSeek V4 Pro | V4 Flash |
111
+ |---|---:|---:|---:|---:|---:|---:|
112
+ | Fact coverage | 24 | 24 | 24 | 23 | 24 | 23 |
113
+ | Fact fidelity | 25 | 25 | 25 | 25 | 25 | 25 |
114
+ | Structure | 15 | 15 | 15 | 14 | 15 | 14 |
115
+ | URL | 5 | 5 | 5 | 5 | 5 | 5 |
116
+ | Evidence link | 10 | 10 | 10 | 10 | 10 | 7 |
117
+ | Section boundary | 9 | 9 | 8 | 9 | 9 | 3 |
118
+ | Modeling | 9 | 9 | 8 | 8 | 7 | 6 |
119
+ | **Total** | **97** | **97** | **95** | **94** | **95** | **83** |
120
+ | Duration | 5m44s | 4m34s | 20m27s | 20m20s | ~9m28s | 2m37s |
121
+
122
+ > GPT 5.5 xh fast / GPT 5.5 m fast run on Codex; Opus 4.8 / Sonnet 4.6 / DeepSeek V4 Pro / V4 Flash run on Claude.
121
123
 
122
124
  ## About this repository
123
125
 
@@ -105,17 +105,19 @@ bun add -g @c4a/context-cli
105
105
 
106
106
  下方数据来自一次并发基准测试:每个模型在同一份 10 篇业务文档语料上端到端跑完 c4a 全流程(capture → align → compile),无人工介入。质量按 7 个维度评分(总分 100)反映产物知识库;耗时覆盖 `context init` 到 `compile close` 的全流程。
107
107
 
108
- | 维度 | Opus 4.8(Claude) | GPT 5.5(Codex) | Opus 4.7(Claude) | DeepSeek V4 Pro(Claude) |
109
- |---|---:|---:|---:|---:|
110
- | 事实覆盖(25) | 23 | 23 | 22 | 18 |
111
- | 事实忠实(25 | 24 | 23 | 19 | 18 |
112
- | 结构语义(15 | 14 | 13 | 8 | 7 |
113
- | URL5 | 5 | 4 | 2 | 5 |
114
- | Source ref(10 | 9 | 9 | 7 | 6 |
115
- | Section 边界(10) | 9 | 10 | 7 | 5 |
116
- | Schema(10) | 9 | 10 | 9 | 7 |
117
- | **总分** | **93** | **92** | **74** | **66** |
118
- | 总耗时 | 30m47s | 10m07s | 19m6s | 14m44s |
108
+ | 维度 | GPT 5.5 xh fast | GPT 5.5 m fast | Opus 4.8 | Sonnet 4.6 | DeepSeek V4 Pro | V4 Flash |
109
+ |---|---:|---:|---:|---:|---:|---:|
110
+ | 事实覆盖 | 24 | 24 | 24 | 23 | 24 | 23 |
111
+ | 事实忠实 | 25 | 25 | 25 | 25 | 25 | 25 |
112
+ | 结构语义 | 15 | 15 | 15 | 14 | 15 | 14 |
113
+ | URL | 5 | 5 | 5 | 5 | 5 | 5 |
114
+ | 证据链 | 10 | 10 | 10 | 10 | 10 | 7 |
115
+ | Section 边界 | 9 | 9 | 8 | 9 | 9 | 3 |
116
+ | 建模 | 9 | 9 | 8 | 8 | 7 | 6 |
117
+ | **总分** | **97** | **97** | **95** | **94** | **95** | **83** |
118
+ | 总耗时 | 5m44s | 4m34s | 20m27s | 20m20s | ~9m28s | 2m37s |
119
+
120
+ > GPT 5.5 xh fast / GPT 5.5 m fast 运行于 Codex;Opus 4.8 / Sonnet 4.6 / DeepSeek V4 Pro / V4 Flash 运行于 Claude。
119
121
 
120
122
  ## 关于本仓库
121
123
 
@@ -57,7 +57,7 @@ Carry the latest envelope forward between iterations. After a successful node cy
57
57
 
58
58
  Run the evidence command returned by the envelope before writing. `views[].expected` identifies the default compact evidence entry, not a separate checklist to exhaust. For compile evidence, prefer the CLI-returned source-ref/scaffold views. They may expose:
59
59
 
60
- - `source_refs_index_command` / `source_refs_command` — compact Node-scoped citation index for drafting; use `items[].block_id` in `source_block_ids[]` when the row is the evidence you will cite.
60
+ - `source_refs_index_command` / `source_refs_command` — compact Node-scoped citation index for drafting; use `items[].block_id` in `source_block_ids[]` when the row is the evidence you will cite. Multiple block ids in one action must be one same-source contiguous citation-eligible run; split around skipped citation-eligible rows or use `--draft-scaffold` when unsure.
61
61
  - `source_refs_detail_command` — detailed source refs with quote previews; open only when the compact index is not enough.
62
62
  - `request_full_text_command` / `--view text` — narrow text view for one block when quote preview is not enough; this is still Node-scoped, not a workspace evidence bundle.
63
63
  - `citable_source_refs[]` — detailed-view refs eligible for draft citations; prefer `block_id` values in `source_block_ids[]`.