@c4a/context-cli 0.5.29-beta.16 → 0.5.29-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +850 -264
- package/package.json +1 -1
- package/plugin/README.md +41 -13
- package/plugin/README_CN.md +41 -13
- package/plugin/assets/workflow-en.png +0 -0
- package/plugin/assets/workflow.png +0 -0
- package/plugin/commands/align.md +9 -1
- package/plugin/commands/compile.md +2 -1
- package/plugin/skills/skill-align-workflow/SKILL.md +8 -2
- package/plugin/skills/skill-compile-close/SKILL.md +2 -2
- package/plugin/skills/skill-semantic-reconcile/SKILL.md +4 -2
package/cli.js
CHANGED
|
@@ -19673,15 +19673,40 @@ function isEvidenceEchoDetail(detail, basisTexts = []) {
|
|
|
19673
19673
|
return true;
|
|
19674
19674
|
return stripped.label !== undefined && PREFIX_ONLY_ECHO_LABEL_RE.test(stripped.label);
|
|
19675
19675
|
}
|
|
19676
|
+
function fencedBlocksIn(value) {
|
|
19677
|
+
return [...value.matchAll(FENCED_DETAIL_BLOCK_RE)].map((match) => normalizeMarkdown(match[0]).trim()).filter((block) => block.length > 0);
|
|
19678
|
+
}
|
|
19679
|
+
function basisText(value) {
|
|
19680
|
+
return typeof value === "string" ? value : value.join(`
|
|
19681
|
+
`);
|
|
19682
|
+
}
|
|
19683
|
+
function hasCitedFencedDetail(detail, basisTexts) {
|
|
19684
|
+
const cited = normalizeMarkdown(basisText(basisTexts)).trim();
|
|
19685
|
+
if (cited.length === 0)
|
|
19686
|
+
return false;
|
|
19687
|
+
return fencedBlocksIn(detail).some((block) => cited.includes(block));
|
|
19688
|
+
}
|
|
19689
|
+
function isOnlyCitedFencedDetail(detail, basisTexts) {
|
|
19690
|
+
const normalizedDetail = normalizeMarkdown(detail).trim();
|
|
19691
|
+
if (normalizedDetail.length === 0)
|
|
19692
|
+
return false;
|
|
19693
|
+
const cited = normalizeMarkdown(basisText(basisTexts)).trim();
|
|
19694
|
+
const blocks = fencedBlocksIn(normalizedDetail);
|
|
19695
|
+
if (cited.length === 0 || blocks.length === 0 || !blocks.every((block) => cited.includes(block)))
|
|
19696
|
+
return false;
|
|
19697
|
+
return normalizedDetail.replace(FENCED_DETAIL_BLOCK_RE, "").trim().length === 0;
|
|
19698
|
+
}
|
|
19676
19699
|
function renderableSectionDetail(detail) {
|
|
19677
19700
|
if (detail === undefined || detail.trim().length === 0)
|
|
19678
19701
|
return;
|
|
19679
19702
|
return isEvidenceEchoDetail(detail) ? undefined : detail;
|
|
19680
19703
|
}
|
|
19681
|
-
var EVIDENCE_ECHO_PREFIX_RE, PREFIX_ONLY_ECHO_LABEL_RE;
|
|
19704
|
+
var EVIDENCE_ECHO_PREFIX_RE, PREFIX_ONLY_ECHO_LABEL_RE, FENCED_DETAIL_BLOCK_RE;
|
|
19682
19705
|
var init_sectionDetail = __esm(() => {
|
|
19706
|
+
init_normalize();
|
|
19683
19707
|
EVIDENCE_ECHO_PREFIX_RE = /^\s*(原文|原始文本|原始引用|引用|证据|source|raw|quote|original)\s*[::]\s*/iu;
|
|
19684
19708
|
PREFIX_ONLY_ECHO_LABEL_RE = /^(?:原文|原始文本|原始引用|引用|证据)$/iu;
|
|
19709
|
+
FENCED_DETAIL_BLOCK_RE = /```[^\n`]*\n[\s\S]*?```|~~~[^\n~]*\n[\s\S]*?~~~/gu;
|
|
19685
19710
|
});
|
|
19686
19711
|
|
|
19687
19712
|
// src/lib/nodeRenderer.ts
|
|
@@ -28706,13 +28731,6 @@ function textIsUrlReferenceOnly(content, citedText) {
|
|
|
28706
28731
|
const requiredMatches = Math.max(1, Math.ceil(lexical.contentTermCount * 0.5));
|
|
28707
28732
|
return lexical.matchedTermCount >= requiredMatches && lexical.missingTerms.length <= 2;
|
|
28708
28733
|
}
|
|
28709
|
-
function fencedBlocksIn(text) {
|
|
28710
|
-
return [...text.matchAll(FENCED_CODE_RE2)].map((match) => normalizeMarkdown(match[0]).trim()).filter((block) => block.length > 0);
|
|
28711
|
-
}
|
|
28712
|
-
function hasCitedFencedDetail(detail, citedText) {
|
|
28713
|
-
const cited = normalizeMarkdown(citedText).trim();
|
|
28714
|
-
return fencedBlocksIn(detail).some((block) => cited.includes(block));
|
|
28715
|
-
}
|
|
28716
28734
|
function urlSupportFor(content, citedText) {
|
|
28717
28735
|
const contentUrls = evidenceUrlsIn(content);
|
|
28718
28736
|
const citedUrls = new Set(evidenceUrlsIn(citedText));
|
|
@@ -28806,11 +28824,13 @@ function supportLexicalOverlap(content, citedText) {
|
|
|
28806
28824
|
function hardTermPresent(term, citedText, citedHardTerms) {
|
|
28807
28825
|
if (citedHardTerms.has(term) || normalizedToken(citedText).includes(term))
|
|
28808
28826
|
return true;
|
|
28809
|
-
const slashParts = term.split("/").map(normalizedToken).filter((part) => part.length > 0);
|
|
28810
|
-
if (slashParts.length <= 1)
|
|
28811
|
-
return false;
|
|
28812
28827
|
const normalizedCited = normalizedToken(citedText);
|
|
28813
|
-
|
|
28828
|
+
const slashParts = term.split("/").map(normalizedToken).filter((part) => part.length > 0);
|
|
28829
|
+
if (slashParts.length > 1) {
|
|
28830
|
+
return slashParts.every((part) => citedHardTerms.has(part) || normalizedCited.includes(part));
|
|
28831
|
+
}
|
|
28832
|
+
const hyphenParts = term.includes("-") && !/[0-9._/:#]/u.test(term) ? term.split("-").map(normalizedToken).filter((part) => part.length > 1) : [];
|
|
28833
|
+
return hyphenParts.length > 1 && hyphenParts.every((part) => citedHardTerms.has(part) || normalizedCited.includes(part));
|
|
28814
28834
|
}
|
|
28815
28835
|
function thresholdsForKind(kind) {
|
|
28816
28836
|
return kind === "spec" || kind === "comparison" || kind === "decision" ? { supported: 0.5, weak: 0.3 } : DEFAULT_THRESHOLDS;
|
|
@@ -28872,8 +28892,7 @@ function sourceTextSupportDiagnostic(content, citedText, thresholds = DEFAULT_TH
|
|
|
28872
28892
|
const requiredMatches = Math.max(1, Math.ceil(contentTermCount * thresholds.supported));
|
|
28873
28893
|
const overlapRatio = matchedTermCount / contentTermCount;
|
|
28874
28894
|
const shortText = contentTermCount <= 10;
|
|
28875
|
-
const
|
|
28876
|
-
const baseVerdict = missingHardTerms.length > 0 ? "unsupported" : overlapRatio >= thresholds.supported || shortText && lexical.missingTerms.length <= 2 ? "supported" : overlapRatio >= thresholds.weak || shortText && lexical.missingTerms.length <= 4 ? "weak" : hasHardFacts && matchedTermCount > 0 ? "weak" : "unsupported";
|
|
28895
|
+
const baseVerdict = missingHardTerms.length > 0 ? "unsupported" : overlapRatio >= thresholds.supported || shortText && lexical.missingTerms.length <= 2 ? "supported" : overlapRatio >= thresholds.weak || shortText && lexical.missingTerms.length <= 4 ? "weak" : matchedTermCount > 0 ? "weak" : "unsupported";
|
|
28877
28896
|
const urlAwareVerdict = urlSupport.contentUrls.length > 0 && urlSupport.missingUrls.length === 0 && missingHardTerms.length === 0 ? urlSupport.referenceOnly && (baseVerdict === "weak" || baseVerdict === "unsupported") ? "supported" : baseVerdict === "unsupported" ? "weak" : baseVerdict : baseVerdict;
|
|
28878
28897
|
const verdict = missingPlaceholderTerms.length > 0 && urlAwareVerdict === "supported" ? "weak" : urlAwareVerdict;
|
|
28879
28898
|
return {
|
|
@@ -29067,13 +29086,14 @@ function weakSourceSupportGuidance(action, verdict) {
|
|
|
29067
29086
|
if (verdict !== "weak")
|
|
29068
29087
|
return "";
|
|
29069
29088
|
if (action === "keep_separate") {
|
|
29070
|
-
return " Key facts match, but support is weak; ask the user to confirm the summary/compression during review, then apply the final keep_separate decision marked decided_by: user.";
|
|
29089
|
+
return " Key facts match, but support is weak; ask the user to confirm the summary/compression during review, then apply the final keep_separate decision marked decided_by: user. Auto mode or general permission to continue is not confirmation.";
|
|
29071
29090
|
}
|
|
29072
29091
|
return " Key facts match, but this action rewrites or reanchors existing knowledge and requires direct support; choose source_ref/source_refs from the prepared evidence, split the claim, or ask the user before choosing a different final action.";
|
|
29073
29092
|
}
|
|
29074
|
-
var DEFAULT_THRESHOLDS, CJK_ONLY_RE, CJK_CHAR_RE, URL_RE2,
|
|
29093
|
+
var DEFAULT_THRESHOLDS, CJK_ONLY_RE, CJK_CHAR_RE, URL_RE2, TRAILING_URL_PUNCTUATION_RE, TRACKING_QUERY_PARAM_RE, EN_URL_REFERENCE_LABEL_RE, CJK_URL_REFERENCE_LABEL_RE, CONSTRAINT_PATTERNS, PLACEHOLDER_ANCHOR_PARTS;
|
|
29075
29094
|
var init_sourceSupport = __esm(() => {
|
|
29076
29095
|
init_normalize();
|
|
29096
|
+
init_sectionDetail();
|
|
29077
29097
|
init_sourceRef();
|
|
29078
29098
|
init_ref();
|
|
29079
29099
|
init_rawBlocks();
|
|
@@ -29082,7 +29102,6 @@ var init_sourceSupport = __esm(() => {
|
|
|
29082
29102
|
CJK_ONLY_RE = /^[\u4e00-\u9fff]+$/u;
|
|
29083
29103
|
CJK_CHAR_RE = /[\u4e00-\u9fff]/gu;
|
|
29084
29104
|
URL_RE2 = /https?:\/\/[^\s)\]}>"')】》」』,。;:!?]+/giu;
|
|
29085
|
-
FENCED_CODE_RE2 = /```[^\n`]*\n[\s\S]*?```/gu;
|
|
29086
29105
|
TRAILING_URL_PUNCTUATION_RE = /[.,;:!?)\]\}>"',。;:!?)】》」』]+$/u;
|
|
29087
29106
|
TRACKING_QUERY_PARAM_RE = /^(?:utm_|fbclid$|gclid$|mc_cid$|mc_eid$)/iu;
|
|
29088
29107
|
EN_URL_REFERENCE_LABEL_RE = /\b(?:url|urls|link|links|doc|docs|document|documents|documentation|reference|references|page|pages|guide|guides|entry|entries|website|official|source|sources)\b/giu;
|
|
@@ -30012,6 +30031,33 @@ function workflowPayloadReceipt(record, extraOrOptions = []) {
|
|
|
30012
30031
|
next_command: options.nextCommand ?? showCommand
|
|
30013
30032
|
};
|
|
30014
30033
|
}
|
|
30034
|
+
function stalePayloadAgentHints(input) {
|
|
30035
|
+
if (input.payload === "align-coarse-read") {
|
|
30036
|
+
const ledgerDigest = input.workflowInputDigests?.["align-candidate-ledger"];
|
|
30037
|
+
const ledgerCommand = ledgerDigest === undefined ? `context workflow show --payload align-candidate-ledger --scope ${shellQuote(input.resolvedScopeId)} --view ledger --unwrap --format json` : [
|
|
30038
|
+
"context workflow show --payload align-candidate-ledger",
|
|
30039
|
+
`--scope ${shellQuote(input.resolvedScopeId)}`,
|
|
30040
|
+
`--digest ${shellQuote(ledgerDigest)}`,
|
|
30041
|
+
"--view ledger",
|
|
30042
|
+
"--unwrap",
|
|
30043
|
+
"--format json"
|
|
30044
|
+
].join(" ");
|
|
30045
|
+
return [{
|
|
30046
|
+
code: "workflow-input-digest-stale",
|
|
30047
|
+
severity: "error",
|
|
30048
|
+
message: "align-coarse-read is a latest checkpoint; earlier per-source reading notes are stored in align-candidate-ledger.source_readings.",
|
|
30049
|
+
next_action: "Read the align-candidate-ledger ledger view, optionally adding --source <source_id> to inspect a specific source reading.",
|
|
30050
|
+
command: ledgerCommand
|
|
30051
|
+
}];
|
|
30052
|
+
}
|
|
30053
|
+
return [{
|
|
30054
|
+
code: "workflow-input-digest-stale",
|
|
30055
|
+
severity: "error",
|
|
30056
|
+
message: "The submitted payload digest does not match the current workflow payload.",
|
|
30057
|
+
next_action: "Run `context workflow show --payload <name>` again and retry with the returned digest.",
|
|
30058
|
+
command: "context workflow show --payload <name>"
|
|
30059
|
+
}];
|
|
30060
|
+
}
|
|
30015
30061
|
async function writeWorkflowPayloadWithDigest(input) {
|
|
30016
30062
|
const path8 = workflowPayloadPath(input);
|
|
30017
30063
|
const body2 = encodePayload(input.value, input.format);
|
|
@@ -30059,13 +30105,7 @@ async function readWorkflowPayloadByDigest(input) {
|
|
|
30059
30105
|
workflow_id: input.workflowId,
|
|
30060
30106
|
scope_id: scopeId,
|
|
30061
30107
|
payload: input.payload,
|
|
30062
|
-
agent_hints:
|
|
30063
|
-
code: "workflow-input-digest-stale",
|
|
30064
|
-
severity: "error",
|
|
30065
|
-
message: "The submitted payload digest does not match the current workflow payload.",
|
|
30066
|
-
next_action: "Run `context workflow show --payload <name>` again and retry with the returned digest.",
|
|
30067
|
-
command: "context workflow show --payload <name>"
|
|
30068
|
-
}]
|
|
30108
|
+
agent_hints: stalePayloadAgentHints({ ...input, resolvedScopeId: scopeId })
|
|
30069
30109
|
});
|
|
30070
30110
|
}
|
|
30071
30111
|
return {
|
|
@@ -32047,8 +32087,11 @@ async function assertNoUnresolvedHighSignalCoverage(ctxDir) {
|
|
|
32047
32087
|
code: "coverage-high-signal-unresolved",
|
|
32048
32088
|
severity: "error",
|
|
32049
32089
|
message: `${unresolved.length} high-signal coverage candidate(s) must be disposed before close.`,
|
|
32050
|
-
next_action: "Read the node-scoped coverage-candidates payload, submit a
|
|
32051
|
-
command: `context workflow show --payload coverage-candidates --scope ${nodeRunIdForSlug(unresolved[0].node_slug)} --view coverage
|
|
32090
|
+
next_action: "Read the node-scoped coverage-candidates payload. If the remaining candidates are intentionally excluded for this node, run --coverage-skip-unresolved with an audit reason; otherwise submit a targeted disposition patch.",
|
|
32091
|
+
command: `context workflow show --payload coverage-candidates --scope ${nodeRunIdForSlug(unresolved[0].node_slug)} --view coverage`,
|
|
32092
|
+
diagnostics: {
|
|
32093
|
+
bulk_skip_command: `context compile --coverage-skip-unresolved --coverage-disposition-node ${unresolved[0].node_slug} --payload-digest <digest> --reason "<reason>"`
|
|
32094
|
+
}
|
|
32052
32095
|
}]
|
|
32053
32096
|
});
|
|
32054
32097
|
}
|
|
@@ -35202,7 +35245,7 @@ var init_agentHintRegistry = __esm(() => {
|
|
|
35202
35245
|
"verify-section-issues-run-compile",
|
|
35203
35246
|
"verify-source-coverage-review",
|
|
35204
35247
|
"verify-structural-issues-run-align",
|
|
35205
|
-
"weak-source-support-
|
|
35248
|
+
"weak-source-support-warning",
|
|
35206
35249
|
"workflow-abandon-id-mismatch",
|
|
35207
35250
|
"workflow-active-node-run-rejected",
|
|
35208
35251
|
"workflow-aggregate-summary",
|
|
@@ -35220,6 +35263,7 @@ var init_agentHintRegistry = __esm(() => {
|
|
|
35220
35263
|
"workflow-input-digest-stale",
|
|
35221
35264
|
"workflow-input-digests-empty",
|
|
35222
35265
|
"workflow-ledger-summary",
|
|
35266
|
+
"workflow-ledger-source-filter",
|
|
35223
35267
|
"workflow-ownership-by-node-summary",
|
|
35224
35268
|
"workflow-payload-scope-ambiguous",
|
|
35225
35269
|
"workflow-payload-view-unsupported",
|
|
@@ -35271,7 +35315,7 @@ var init_agentHintRegistry = __esm(() => {
|
|
|
35271
35315
|
severity: "warning",
|
|
35272
35316
|
description: "High-signal coverage candidates remain unresolved before close.",
|
|
35273
35317
|
trigger: "A compile context/apply result sees unresolved high-signal coverage candidates for the current node.",
|
|
35274
|
-
command:
|
|
35318
|
+
command: 'context compile --coverage-skip-unresolved --coverage-disposition-node <slug> --payload-digest <digest> --reason "<reason>"'
|
|
35275
35319
|
},
|
|
35276
35320
|
{
|
|
35277
35321
|
code: "review-safe-defaults-partial",
|
|
@@ -35441,8 +35485,8 @@ function excerptAround(value, index, radius = 80) {
|
|
|
35441
35485
|
const suffix = end < value.length ? "..." : "";
|
|
35442
35486
|
return `${prefix}${value.slice(start2, end)}${suffix}`;
|
|
35443
35487
|
}
|
|
35444
|
-
function normalizedDivergence(
|
|
35445
|
-
const basis = normalizeEvidenceText(
|
|
35488
|
+
function normalizedDivergence(basisText2, draftText) {
|
|
35489
|
+
const basis = normalizeEvidenceText(basisText2);
|
|
35446
35490
|
const draft = normalizeEvidenceText(draftText);
|
|
35447
35491
|
const max = Math.min(basis.length, draft.length);
|
|
35448
35492
|
let offset = 0;
|
|
@@ -35462,8 +35506,8 @@ function normalizedExtractSegments(value) {
|
|
|
35462
35506
|
function normalizeSegmentSearchText(value) {
|
|
35463
35507
|
return normalizeEvidenceText(value).replace(/[.,;:!?,。;:!?]+/gu, " ").replace(/\s+/gu, " ").trim();
|
|
35464
35508
|
}
|
|
35465
|
-
function extractModeHint(field,
|
|
35466
|
-
const basis = normalizeSegmentSearchText(
|
|
35509
|
+
function extractModeHint(field, basisText2, draftText) {
|
|
35510
|
+
const basis = normalizeSegmentSearchText(basisText2);
|
|
35467
35511
|
const draftSegments = normalizedExtractSegments(draftText);
|
|
35468
35512
|
const segments = draftSegments.map((segment, index) => ({
|
|
35469
35513
|
detail_order: index,
|
|
@@ -35714,7 +35758,7 @@ async function resolveBasisSpans(ctxDir, spans, slug, context, label) {
|
|
|
35714
35758
|
function assertExtractContent(action, slug, context, label, resolvedBasisSpans, state) {
|
|
35715
35759
|
if (action.content_mode !== "extract")
|
|
35716
35760
|
return;
|
|
35717
|
-
const
|
|
35761
|
+
const basisText2 = resolvedBasisSpans.map((span) => span.text).join(`
|
|
35718
35762
|
`);
|
|
35719
35763
|
for (const [field, value] of [
|
|
35720
35764
|
["content", action.content],
|
|
@@ -35722,16 +35766,16 @@ function assertExtractContent(action, slug, context, label, resolvedBasisSpans,
|
|
|
35722
35766
|
]) {
|
|
35723
35767
|
if (typeof value !== "string" || value.trim().length === 0)
|
|
35724
35768
|
continue;
|
|
35725
|
-
const normalizedMatch = normalizedIncludes(
|
|
35726
|
-
const exactMatch =
|
|
35769
|
+
const normalizedMatch = normalizedIncludes(basisText2, value);
|
|
35770
|
+
const exactMatch = basisText2.includes(value);
|
|
35727
35771
|
if (!normalizedMatch) {
|
|
35728
|
-
const modeHint = extractModeHint(field,
|
|
35772
|
+
const modeHint = extractModeHint(field, basisText2, value);
|
|
35729
35773
|
throw draftError(slug, context, `extract ${field} must be a normalized substring of the cited source text`, {
|
|
35730
35774
|
path: `${label}.${field}`,
|
|
35731
35775
|
reasonCode: `extract-${field}-not-in-basis`,
|
|
35732
35776
|
diffHint: modeHint !== null ? String(modeHint.next_action) : `${field} must be copied from the cited source text as one contiguous normalized substring when content_mode=extract. Use the diagnostics.divergence excerpts to fix the first mismatch, or switch to minimal_paraphrase when you intentionally rewrite or reorder text.`,
|
|
35733
35777
|
diagnostics: {
|
|
35734
|
-
divergence: normalizedDivergence(
|
|
35778
|
+
divergence: normalizedDivergence(basisText2, value),
|
|
35735
35779
|
...modeHint !== null ? { extract_mode_hint: modeHint } : {}
|
|
35736
35780
|
}
|
|
35737
35781
|
});
|
|
@@ -35753,6 +35797,9 @@ function assertNonEchoDetail(action, slug, context, label, resolvedBasisSpans) {
|
|
|
35753
35797
|
const basisTexts = resolvedBasisSpans.map((span) => span.text);
|
|
35754
35798
|
if (!isEvidenceEchoDetail(action.detail, basisTexts))
|
|
35755
35799
|
return;
|
|
35800
|
+
const sectionKind = "kind" in action ? action.kind : context.existing?.sections.find((section) => section.id === action.target_section_id)?.kind;
|
|
35801
|
+
if (sectionKind === "example" && isOnlyCitedFencedDetail(action.detail, basisTexts))
|
|
35802
|
+
return;
|
|
35756
35803
|
throw draftError(slug, context, `${label}.detail must not echo raw cited evidence`, {
|
|
35757
35804
|
path: `${label}.detail`,
|
|
35758
35805
|
reasonCode: "detail-evidence-echo",
|
|
@@ -36163,30 +36210,141 @@ async function archiveOutputArtifacts(options) {
|
|
|
36163
36210
|
}
|
|
36164
36211
|
var init_outputArchive = () => {};
|
|
36165
36212
|
|
|
36166
|
-
// src/workflow/
|
|
36213
|
+
// src/workflow/compileCloseFinalizedNodes.ts
|
|
36167
36214
|
import { existsSync as existsSync43 } from "node:fs";
|
|
36168
|
-
import { readFile as readFile51, readdir as readdir19, rm as rm13 } from "node:fs/promises";
|
|
36169
36215
|
import { join as join52 } from "node:path";
|
|
36216
|
+
function finalizedContainerChildren(nodes, parent) {
|
|
36217
|
+
if (parent.type !== "domain")
|
|
36218
|
+
return [];
|
|
36219
|
+
return nodes.filter((node2) => node2.contains_parent === parent.slug).sort((left, right) => left.slug.localeCompare(right.slug));
|
|
36220
|
+
}
|
|
36221
|
+
function finalizedNodePath(ctxDir, node2) {
|
|
36222
|
+
return join52(ctxDir, "knowledge", node2.type, `${node2.slug}.md`);
|
|
36223
|
+
}
|
|
36224
|
+
function isExplicitNoWriteNode(node2) {
|
|
36225
|
+
return node2.planned_sections !== undefined && node2.planned_sections.length === 0;
|
|
36226
|
+
}
|
|
36227
|
+
function hasFinalizedGraphLink(ownership, node2) {
|
|
36228
|
+
if (typeof node2.contains_parent === "string" && node2.contains_parent.length > 0)
|
|
36229
|
+
return true;
|
|
36230
|
+
if ((ownership.nodes ?? []).some((candidate) => candidate.contains_parent === node2.slug))
|
|
36231
|
+
return true;
|
|
36232
|
+
return (ownership.edges ?? []).some((edge2) => edge2.from === node2.slug || edge2.to === node2.slug);
|
|
36233
|
+
}
|
|
36234
|
+
function hasNoWritePlaceholderSupport(ownership, node2) {
|
|
36235
|
+
return (node2.context_sources?.length ?? 0) > 0 || hasFinalizedGraphLink(ownership, node2);
|
|
36236
|
+
}
|
|
36237
|
+
async function ensureFinalizedContainerDomains(ctxDir, now) {
|
|
36238
|
+
const ownership = await readCurrentSourceOwnership(ctxDir);
|
|
36239
|
+
const nodes = ownership?.nodes ?? [];
|
|
36240
|
+
let created = 0;
|
|
36241
|
+
for (const node2 of nodes) {
|
|
36242
|
+
const children = finalizedContainerChildren(nodes, node2);
|
|
36243
|
+
if (children.length === 0)
|
|
36244
|
+
continue;
|
|
36245
|
+
if (existsSync43(finalizedNodePath(ctxDir, node2)))
|
|
36246
|
+
continue;
|
|
36247
|
+
await mdriveNodeCreate({
|
|
36248
|
+
ctxDir,
|
|
36249
|
+
input: {
|
|
36250
|
+
node: {
|
|
36251
|
+
id: node2.slug,
|
|
36252
|
+
title: node2.title,
|
|
36253
|
+
type: node2.type,
|
|
36254
|
+
tags: node2.tags,
|
|
36255
|
+
sources: [...node2.sources],
|
|
36256
|
+
updated: now.toISOString().slice(0, 10),
|
|
36257
|
+
...node2.context_sources !== undefined ? { context_sources: [...node2.context_sources] } : {},
|
|
36258
|
+
...node2.summary !== undefined ? { summary: node2.summary } : {},
|
|
36259
|
+
...node2.domain_gate !== undefined ? { domain_gate: node2.domain_gate } : {}
|
|
36260
|
+
},
|
|
36261
|
+
body: "",
|
|
36262
|
+
sections: [],
|
|
36263
|
+
containsList: children.map((child) => ({
|
|
36264
|
+
slug: child.slug,
|
|
36265
|
+
title: child.title,
|
|
36266
|
+
href: `../${child.type}/${child.slug}.md`
|
|
36267
|
+
})),
|
|
36268
|
+
children: []
|
|
36269
|
+
}
|
|
36270
|
+
});
|
|
36271
|
+
created += 1;
|
|
36272
|
+
}
|
|
36273
|
+
return created;
|
|
36274
|
+
}
|
|
36275
|
+
async function ensureFinalizedNoWritePlaceholders(ctxDir, now) {
|
|
36276
|
+
const ownership = await readCurrentSourceOwnership(ctxDir);
|
|
36277
|
+
if (!ownership)
|
|
36278
|
+
return 0;
|
|
36279
|
+
const nodes = ownership.nodes ?? [];
|
|
36280
|
+
let created = 0;
|
|
36281
|
+
for (const node2 of nodes) {
|
|
36282
|
+
if (!isExplicitNoWriteNode(node2))
|
|
36283
|
+
continue;
|
|
36284
|
+
if (node2.sources.length > 0)
|
|
36285
|
+
continue;
|
|
36286
|
+
if (!hasNoWritePlaceholderSupport(ownership, node2))
|
|
36287
|
+
continue;
|
|
36288
|
+
if (existsSync43(finalizedNodePath(ctxDir, node2)))
|
|
36289
|
+
continue;
|
|
36290
|
+
const children = finalizedContainerChildren(nodes, node2);
|
|
36291
|
+
await mdriveNodeCreate({
|
|
36292
|
+
ctxDir,
|
|
36293
|
+
input: {
|
|
36294
|
+
node: {
|
|
36295
|
+
id: node2.slug,
|
|
36296
|
+
title: node2.title,
|
|
36297
|
+
type: node2.type,
|
|
36298
|
+
tags: [...node2.tags],
|
|
36299
|
+
sources: [],
|
|
36300
|
+
updated: now.toISOString().slice(0, 10),
|
|
36301
|
+
...node2.context_sources !== undefined ? { context_sources: [...node2.context_sources] } : {},
|
|
36302
|
+
...node2.summary !== undefined ? { summary: node2.summary } : {},
|
|
36303
|
+
...node2.language !== undefined ? { language: node2.language } : {}
|
|
36304
|
+
},
|
|
36305
|
+
body: "",
|
|
36306
|
+
sections: [],
|
|
36307
|
+
containsList: children.map((child) => ({
|
|
36308
|
+
slug: child.slug,
|
|
36309
|
+
title: child.title,
|
|
36310
|
+
href: `../${child.type}/${child.slug}.md`
|
|
36311
|
+
})),
|
|
36312
|
+
children: []
|
|
36313
|
+
}
|
|
36314
|
+
});
|
|
36315
|
+
created += 1;
|
|
36316
|
+
}
|
|
36317
|
+
return created;
|
|
36318
|
+
}
|
|
36319
|
+
var init_compileCloseFinalizedNodes = __esm(() => {
|
|
36320
|
+
init_node2();
|
|
36321
|
+
init_sourceOwnership();
|
|
36322
|
+
});
|
|
36323
|
+
|
|
36324
|
+
// src/workflow/compileClose.ts
|
|
36325
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
36326
|
+
import { readFile as readFile51, readdir as readdir19, rm as rm13 } from "node:fs/promises";
|
|
36327
|
+
import { join as join53 } from "node:path";
|
|
36170
36328
|
function knowledgePath(ctxDir, name) {
|
|
36171
|
-
return
|
|
36329
|
+
return join53(ctxDir, "knowledge", name);
|
|
36172
36330
|
}
|
|
36173
36331
|
async function snapshotFile(path8) {
|
|
36174
|
-
return
|
|
36332
|
+
return existsSync44(path8) ? { path: path8, content: await readFile51(path8, "utf8") } : { path: path8 };
|
|
36175
36333
|
}
|
|
36176
36334
|
async function snapshotCompileCloseFiles(ctxDir) {
|
|
36177
36335
|
const paths = new Set([
|
|
36178
36336
|
knowledgePath(ctxDir, "_index.md"),
|
|
36179
36337
|
knowledgePath(ctxDir, "changelog.md"),
|
|
36180
|
-
|
|
36338
|
+
join53(ctxDir, "knowledge", "_edges.yaml")
|
|
36181
36339
|
]);
|
|
36182
36340
|
for (const type of NODE_TYPES) {
|
|
36183
|
-
const dir =
|
|
36184
|
-
if (!
|
|
36341
|
+
const dir = join53(ctxDir, "knowledge", type);
|
|
36342
|
+
if (!existsSync44(dir))
|
|
36185
36343
|
continue;
|
|
36186
36344
|
const entries = await readdir19(dir, { withFileTypes: true });
|
|
36187
36345
|
for (const entry of entries) {
|
|
36188
36346
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
36189
|
-
paths.add(
|
|
36347
|
+
paths.add(join53(dir, entry.name));
|
|
36190
36348
|
}
|
|
36191
36349
|
}
|
|
36192
36350
|
}
|
|
@@ -36205,7 +36363,7 @@ async function restoreCompileCloseSnapshot(snapshot) {
|
|
|
36205
36363
|
}
|
|
36206
36364
|
async function readLastCompileAt2(ctxDir) {
|
|
36207
36365
|
const changelog = knowledgePath(ctxDir, "changelog.md");
|
|
36208
|
-
if (!
|
|
36366
|
+
if (!existsSync44(changelog))
|
|
36209
36367
|
return null;
|
|
36210
36368
|
const lines = (await readFile51(changelog, "utf8")).split(`
|
|
36211
36369
|
`).filter((line) => line.includes("[compile]"));
|
|
@@ -36242,7 +36400,7 @@ async function compileLedgerAppliedNodesAfter(ctxDir, timestamp) {
|
|
|
36242
36400
|
}
|
|
36243
36401
|
async function compileDraftScratchEntries(ctxDir) {
|
|
36244
36402
|
const outputDir = workflowOutputDir(ctxDir, "compile");
|
|
36245
|
-
if (!
|
|
36403
|
+
if (!existsSync44(outputDir))
|
|
36246
36404
|
return [];
|
|
36247
36405
|
const entries = await readdir19(outputDir, { withFileTypes: true });
|
|
36248
36406
|
const out2 = [];
|
|
@@ -36251,7 +36409,7 @@ async function compileDraftScratchEntries(ctxDir) {
|
|
|
36251
36409
|
continue;
|
|
36252
36410
|
const match = /^compile\.(.+)\.draft\.(?:json|ya?ml)$/u.exec(entry.name);
|
|
36253
36411
|
if (match?.[1])
|
|
36254
|
-
out2.push({ slug: match[1], path:
|
|
36412
|
+
out2.push({ slug: match[1], path: join53(outputDir, entry.name) });
|
|
36255
36413
|
}
|
|
36256
36414
|
return out2;
|
|
36257
36415
|
}
|
|
@@ -36331,54 +36489,11 @@ async function assertCompileCloseVerifyOk(ctxDir, allowedActiveEmptySlugs = new
|
|
|
36331
36489
|
});
|
|
36332
36490
|
}
|
|
36333
36491
|
}
|
|
36334
|
-
function finalizedContainerChildren(nodes, parent) {
|
|
36335
|
-
if (parent.type !== "domain")
|
|
36336
|
-
return [];
|
|
36337
|
-
return nodes.filter((node2) => node2.contains_parent === parent.slug).sort((left, right) => left.slug.localeCompare(right.slug));
|
|
36338
|
-
}
|
|
36339
|
-
async function ensureFinalizedContainerDomains(ctxDir, now) {
|
|
36340
|
-
const ownership = await readCurrentSourceOwnership(ctxDir);
|
|
36341
|
-
const nodes = ownership?.nodes ?? [];
|
|
36342
|
-
let created = 0;
|
|
36343
|
-
for (const node2 of nodes) {
|
|
36344
|
-
const children = finalizedContainerChildren(nodes, node2);
|
|
36345
|
-
if (children.length === 0)
|
|
36346
|
-
continue;
|
|
36347
|
-
if (existsSync43(join52(ctxDir, "knowledge", "domain", `${node2.slug}.md`)))
|
|
36348
|
-
continue;
|
|
36349
|
-
await mdriveNodeCreate({
|
|
36350
|
-
ctxDir,
|
|
36351
|
-
input: {
|
|
36352
|
-
node: {
|
|
36353
|
-
id: node2.slug,
|
|
36354
|
-
title: node2.title,
|
|
36355
|
-
type: node2.type,
|
|
36356
|
-
tags: node2.tags,
|
|
36357
|
-
sources: [...node2.sources],
|
|
36358
|
-
updated: now.toISOString().slice(0, 10),
|
|
36359
|
-
...node2.context_sources !== undefined ? { context_sources: [...node2.context_sources] } : {},
|
|
36360
|
-
...node2.summary !== undefined ? { summary: node2.summary } : {},
|
|
36361
|
-
...node2.domain_gate !== undefined ? { domain_gate: node2.domain_gate } : {}
|
|
36362
|
-
},
|
|
36363
|
-
body: "",
|
|
36364
|
-
sections: [],
|
|
36365
|
-
containsList: children.map((child) => ({
|
|
36366
|
-
slug: child.slug,
|
|
36367
|
-
title: child.title,
|
|
36368
|
-
href: `../${child.type}/${child.slug}.md`
|
|
36369
|
-
})),
|
|
36370
|
-
children: []
|
|
36371
|
-
}
|
|
36372
|
-
});
|
|
36373
|
-
created += 1;
|
|
36374
|
-
}
|
|
36375
|
-
return created;
|
|
36376
|
-
}
|
|
36377
36492
|
async function assertFinalizedKnowledgeNodesMaterialized(ctxDir) {
|
|
36378
36493
|
const ownership = await readCurrentSourceOwnership(ctxDir);
|
|
36379
36494
|
if (!ownership)
|
|
36380
36495
|
return;
|
|
36381
|
-
const missing = (ownership.nodes ?? []).filter((node2) => !
|
|
36496
|
+
const missing = (ownership.nodes ?? []).filter((node2) => !existsSync44(finalizedNodePath(ctxDir, node2)));
|
|
36382
36497
|
if (missing.length === 0)
|
|
36383
36498
|
return;
|
|
36384
36499
|
const activeSlugs = finalizedSlugSet(ownership);
|
|
@@ -36522,8 +36637,8 @@ async function compileClose(options) {
|
|
|
36522
36637
|
}
|
|
36523
36638
|
}
|
|
36524
36639
|
async function compileCloseInner(options) {
|
|
36525
|
-
const rebuiltIndex = !
|
|
36526
|
-
const rebuiltChangelog = !
|
|
36640
|
+
const rebuiltIndex = !existsSync44(knowledgePath(options.ctxDir, "_index.md"));
|
|
36641
|
+
const rebuiltChangelog = !existsSync44(knowledgePath(options.ctxDir, "changelog.md"));
|
|
36527
36642
|
const now = options.now ?? new Date;
|
|
36528
36643
|
const sourcesFile = await loadSources(options.ctxDir);
|
|
36529
36644
|
const workspaceRoot = workspaceRootFromCtxDir(options.ctxDir);
|
|
@@ -36551,6 +36666,7 @@ async function compileCloseInner(options) {
|
|
|
36551
36666
|
const coverageStatus = await readCoverageWorkspaceStatus(options.ctxDir);
|
|
36552
36667
|
const retractedNodes = await retractRemovedFinalizedNodes(options.ctxDir);
|
|
36553
36668
|
const createdContainerDomains = await ensureFinalizedContainerDomains(options.ctxDir, now);
|
|
36669
|
+
await ensureFinalizedNoWritePlaceholders(options.ctxDir, now);
|
|
36554
36670
|
await assertFinalizedKnowledgeNodesMaterialized(options.ctxDir);
|
|
36555
36671
|
await projectFinalizedGraph(options.ctxDir);
|
|
36556
36672
|
const stats = await mdriveWorkspaceStats({ ctxDir: options.ctxDir });
|
|
@@ -36655,21 +36771,21 @@ function coverageDebtItems(status) {
|
|
|
36655
36771
|
}));
|
|
36656
36772
|
}
|
|
36657
36773
|
async function readWorkflowPayloadFiles(ctxDir, payloadBaseName) {
|
|
36658
|
-
const root =
|
|
36659
|
-
if (!
|
|
36774
|
+
const root = join53(ctxDir, "output", "workflows");
|
|
36775
|
+
if (!existsSync44(root))
|
|
36660
36776
|
return [];
|
|
36661
36777
|
const out2 = [];
|
|
36662
36778
|
for (const workflowEntry of await readdir19(root, { withFileTypes: true })) {
|
|
36663
36779
|
if (!workflowEntry.isDirectory())
|
|
36664
36780
|
continue;
|
|
36665
|
-
const workflowDir =
|
|
36781
|
+
const workflowDir = join53(root, workflowEntry.name);
|
|
36666
36782
|
for (const scopeEntry of await readdir19(workflowDir, { withFileTypes: true }).catch(() => [])) {
|
|
36667
36783
|
if (!scopeEntry.isDirectory())
|
|
36668
36784
|
continue;
|
|
36669
|
-
const scopeDir =
|
|
36785
|
+
const scopeDir = join53(workflowDir, scopeEntry.name);
|
|
36670
36786
|
for (const format of ["json", "yaml"]) {
|
|
36671
|
-
const path8 =
|
|
36672
|
-
if (!
|
|
36787
|
+
const path8 = join53(scopeDir, `${payloadBaseName}.${format}`);
|
|
36788
|
+
if (!existsSync44(path8))
|
|
36673
36789
|
continue;
|
|
36674
36790
|
const body2 = await readFile51(path8, "utf8");
|
|
36675
36791
|
out2.push(format === "json" ? JSON.parse(body2) : import_yaml27.default.parse(body2));
|
|
@@ -36781,6 +36897,7 @@ var init_compileClose = __esm(() => {
|
|
|
36781
36897
|
init_workflowPayloadStore();
|
|
36782
36898
|
init_edge2();
|
|
36783
36899
|
init_graphEdges();
|
|
36900
|
+
init_compileCloseFinalizedNodes();
|
|
36784
36901
|
import_yaml27 = __toESM(require_dist(), 1);
|
|
36785
36902
|
});
|
|
36786
36903
|
|
|
@@ -37643,9 +37760,9 @@ var exports_prepareCompile = {};
|
|
|
37643
37760
|
__export(exports_prepareCompile, {
|
|
37644
37761
|
prepareCompileReconcileContext: () => prepareCompileReconcileContext
|
|
37645
37762
|
});
|
|
37646
|
-
import { existsSync as
|
|
37763
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
37647
37764
|
import { readdir as readdir20, readFile as readFile52 } from "node:fs/promises";
|
|
37648
|
-
import { join as
|
|
37765
|
+
import { join as join54 } from "node:path";
|
|
37649
37766
|
function targetSectionSourceRef(node2, sectionId) {
|
|
37650
37767
|
if (!sectionId)
|
|
37651
37768
|
return;
|
|
@@ -37943,7 +38060,7 @@ function isSafeRelativePath(path8) {
|
|
|
37943
38060
|
return path8.trim().length > 0 && !path8.startsWith("/") && !path8.split(/[\\/]+/u).includes("..");
|
|
37944
38061
|
}
|
|
37945
38062
|
async function readDropArchiveManifest2(path8) {
|
|
37946
|
-
if (!
|
|
38063
|
+
if (!existsSync45(path8))
|
|
37947
38064
|
return null;
|
|
37948
38065
|
try {
|
|
37949
38066
|
const parsed = import_yaml28.default.parse(await readFile52(path8, "utf8"));
|
|
@@ -37988,27 +38105,27 @@ async function restoredArchiveCandidatesForNode(ctxDir, targetNode) {
|
|
|
37988
38105
|
const restoredSourceIds = new Set(sourcesFile.sources.filter((source2) => source2.status === "active" && typeof source2.restored_at === "string").map((source2) => source2.id));
|
|
37989
38106
|
if (restoredSourceIds.size === 0)
|
|
37990
38107
|
return [];
|
|
37991
|
-
const sourcesRoot =
|
|
37992
|
-
if (!
|
|
38108
|
+
const sourcesRoot = join54(ctxDir, "archive", "sources");
|
|
38109
|
+
if (!existsSync45(sourcesRoot))
|
|
37993
38110
|
return [];
|
|
37994
38111
|
const out2 = [];
|
|
37995
38112
|
for (const sourceDir of await readdir20(sourcesRoot, { withFileTypes: true })) {
|
|
37996
38113
|
if (!sourceDir.isDirectory())
|
|
37997
38114
|
continue;
|
|
37998
|
-
const sourceRoot =
|
|
38115
|
+
const sourceRoot = join54(sourcesRoot, sourceDir.name);
|
|
37999
38116
|
for (const archiveDir of await readdir20(sourceRoot, { withFileTypes: true })) {
|
|
38000
38117
|
if (!archiveDir.isDirectory())
|
|
38001
38118
|
continue;
|
|
38002
38119
|
const archivePath = `archive/sources/${sourceDir.name}/${archiveDir.name}`;
|
|
38003
|
-
const manifest = await readDropArchiveManifest2(
|
|
38120
|
+
const manifest = await readDropArchiveManifest2(join54(ctxDir, archivePath, "manifest.yaml"));
|
|
38004
38121
|
if (!manifest || !restoredSourceIds.has(manifest.source_id))
|
|
38005
38122
|
continue;
|
|
38006
38123
|
const knowledgeBefore = Array.isArray(manifest.knowledge_before) ? manifest.knowledge_before : [];
|
|
38007
38124
|
for (const relPath of knowledgeBefore) {
|
|
38008
38125
|
if (!isSafeRelativePath(relPath))
|
|
38009
38126
|
continue;
|
|
38010
|
-
const absPath =
|
|
38011
|
-
if (!
|
|
38127
|
+
const absPath = join54(ctxDir, relPath);
|
|
38128
|
+
if (!existsSync45(absPath))
|
|
38012
38129
|
continue;
|
|
38013
38130
|
let parsed;
|
|
38014
38131
|
try {
|
|
@@ -38711,8 +38828,8 @@ var init_prepare = __esm(() => {
|
|
|
38711
38828
|
});
|
|
38712
38829
|
|
|
38713
38830
|
// src/cli.ts
|
|
38714
|
-
import { existsSync as
|
|
38715
|
-
import { dirname as dirname28, join as
|
|
38831
|
+
import { existsSync as existsSync49, readFileSync as readFileSync3, realpathSync } from "node:fs";
|
|
38832
|
+
import { dirname as dirname28, join as join63 } from "node:path";
|
|
38716
38833
|
import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
38717
38834
|
|
|
38718
38835
|
// ../../node_modules/.bun/commander@11.1.0/node_modules/commander/esm.mjs
|
|
@@ -38882,6 +38999,7 @@ This directory is the C4A data root for a knowledge workspace.
|
|
|
38882
38999
|
- Do not pipe or wrap \`context ... --format json\` output through \`python3\`, \`node\`, \`jq\`, \`sed\`, \`cat\`, \`2>&1\`, \`|| echo\`, or similar shell helpers. Run the \`context\` command directly and consume its complete stdout.
|
|
38883
39000
|
- Schema discovery must also go through direct \`context\` commands. Schema commands support \`--format text|json|yaml\`; use readable YAML/text while drafting and JSON when a caller needs machine parsing. Inspect the schema \`enums\` field for allowed tags/actions instead of inventing labels such as \`platform\` or \`tool\`.
|
|
38884
39001
|
- When passing large JSON / YAML / Markdown bodies to the CLI, feed stdin directly into the \`context\` command with a heredoc, for example \`context align --finalize - --digest <segments-digest> <<'JSON'\`. Do not pipe a heredoc through another command, and never redirect heredocs to workspace files.
|
|
39002
|
+
- For large \`context align --finalize\` payloads, use \`block_ownership_defaults[]\` as a compact source-level ownership rule instead of generating temporary JSON files or enumerating hundreds of block rows. Defaults must still declare an explicit \`source_id\`, \`ownership_role\`, owner/visibility fields, and reason when needed; use \`block_ownership[]\` for block-level exceptions. Defaults are a compression mechanism, not permission to assign mixed-source evidence to one owner without review.
|
|
38885
39003
|
- In Claude Code, direct heredoc commands such as \`context compile --draft <slug> --input - --plan <<'JSON'\` may still prompt for permission when only subcommand-specific allow rules are configured. If that happens, ask the user before adding the scoped allow rule \`Bash(context:*)\` to the project \`.claude/settings.local.json\`; do not enable \`bypassPermissions\`.
|
|
38886
39004
|
- Never hand-edit \`raw/\`, \`raw/_sources.yaml\`, \`archive/\`, \`knowledge/\`, \`knowledge/_index.md\`, or \`knowledge/changelog.md\`. Maintain them through \`context\` CLI commands and \`/context:*\` workflows.
|
|
38887
39005
|
- \`decisions/\` stores CLI-managed semantic reconciliation decisions. Do not directly read, search, or write \`decisions/semantic.yaml\`; use \`context reconcile prepare|review|apply|record\`.
|
|
@@ -47457,8 +47575,8 @@ var DOCTOR_ACTIONS = {
|
|
|
47457
47575
|
"coverage-high-signal-unresolved": {
|
|
47458
47576
|
hintCode: "coverage-high-signal-unresolved",
|
|
47459
47577
|
message: (count) => `${count} high-signal coverage issue(s) must be disposed before compile close.`,
|
|
47460
|
-
next_action: "Use context status --format json to find coverage.candidates[].node_slug,
|
|
47461
|
-
command:
|
|
47578
|
+
next_action: "Use context status --format json to find coverage.candidates[].node_slug, read the matching node-scoped coverage payload, then use --coverage-skip-unresolved when all unresolved candidates are intentionally excluded.",
|
|
47579
|
+
command: 'context compile --coverage-skip-unresolved --coverage-disposition-node <slug> --payload-digest <digest> --reason "<reason>"'
|
|
47462
47580
|
},
|
|
47463
47581
|
"coverage-diagnostics-failed": {
|
|
47464
47582
|
hintCode: "coverage-diagnostics-failed",
|
|
@@ -49722,7 +49840,7 @@ function reviewAgentHints(input) {
|
|
|
49722
49840
|
code: "source-support-repair",
|
|
49723
49841
|
severity: "error",
|
|
49724
49842
|
message: `${sourceSupportIssueCount} decision(s) cite raw text that does not support the proposed content.`,
|
|
49725
|
-
next_action: "Do not accept defaults for unsupported items.
|
|
49843
|
+
next_action: "Do not accept defaults for unsupported items. Missing hard facts mean the cited raw text does not cover the proposed content; narrow content, choose source_support.evidence_block_source_ref/candidates, or split the action. Low lexical overlap without missing hard facts is reported as a warning.",
|
|
49726
49844
|
command: "context workflow show --payload prepare --scope <scope-id> --digest <prepare-digest> --view issues --status unsupported --unwrap --format json"
|
|
49727
49845
|
});
|
|
49728
49846
|
}
|
|
@@ -49799,13 +49917,13 @@ function reviewAgentHints(input) {
|
|
|
49799
49917
|
schema: "context schema semantic-decisions --format yaml"
|
|
49800
49918
|
});
|
|
49801
49919
|
}
|
|
49802
|
-
const weakSupportCount = input.questions.filter((question) => question.type === "support_confirmation").length;
|
|
49920
|
+
const weakSupportCount = input.issues.filter((issue) => issue.code === "weak-source-support").length + input.questions.filter((question) => question.type === "support_confirmation").length;
|
|
49803
49921
|
if (weakSupportCount > 0) {
|
|
49804
49922
|
hints.push({
|
|
49805
|
-
code: "weak-source-support-
|
|
49923
|
+
code: "weak-source-support-warning",
|
|
49806
49924
|
severity: "warning",
|
|
49807
49925
|
message: `${weakSupportCount} item(s) are only weakly supported by the cited evidence.`,
|
|
49808
|
-
next_action: "
|
|
49926
|
+
next_action: "Proceed only if the weak summary/compression is acceptable for this workflow; otherwise narrow content or choose/split source_ref/source_refs."
|
|
49809
49927
|
});
|
|
49810
49928
|
}
|
|
49811
49929
|
const exampleDetailQuestionCount = input.questions.filter((question) => question.type === "example_detail_preservation").length;
|
|
@@ -49814,7 +49932,7 @@ function reviewAgentHints(input) {
|
|
|
49814
49932
|
code: "example-detail-preservation",
|
|
49815
49933
|
severity: "warning",
|
|
49816
49934
|
message: `${exampleDetailQuestionCount} example decision(s) cite code/config/command evidence without preserving it in detail.`,
|
|
49817
|
-
next_action: "Move the relevant fenced block into proposed.detail and rerun review, or ask the user to confirm prose-only summary is acceptable."
|
|
49935
|
+
next_action: "Move the relevant fenced block into proposed.detail and rerun review, or ask the user to confirm prose-only summary is acceptable. Auto mode or general permission to continue is not confirmation."
|
|
49818
49936
|
});
|
|
49819
49937
|
}
|
|
49820
49938
|
const urlReferenceQuestionCount = input.questions.filter((question) => question.type === "url_reference_preservation").length;
|
|
@@ -49823,7 +49941,7 @@ function reviewAgentHints(input) {
|
|
|
49823
49941
|
code: "url-reference-preservation",
|
|
49824
49942
|
severity: "warning",
|
|
49825
49943
|
message: `${urlReferenceQuestionCount} decision(s) cite URL evidence without preserving the referenced link.`,
|
|
49826
|
-
next_action: "Move the relevant URL into proposed.detail and rerun review, or ask the user to confirm the link may be omitted."
|
|
49944
|
+
next_action: "Move the relevant URL into proposed.detail and rerun review, or ask the user to confirm the link may be omitted. Auto mode or general permission to continue is not confirmation."
|
|
49827
49945
|
});
|
|
49828
49946
|
}
|
|
49829
49947
|
const unresolvedQuestionCount = input.questions.length;
|
|
@@ -49893,7 +50011,7 @@ function urlReferencePreservationQuestion(input) {
|
|
|
49893
50011
|
...input.decision.proposed?.content !== undefined ? { proposed_content: input.decision.proposed.content } : {},
|
|
49894
50012
|
cited_urls: diagnostic.citedUrls,
|
|
49895
50013
|
missing_urls: diagnostic.missingUrls,
|
|
49896
|
-
prompt: "The cited raw evidence includes URL(s), but this document/link/reference claim does not preserve them in proposed content or detail. " + "Add the relevant URL(s) to proposed.detail and rerun review, or ask the user to confirm that omitting the link is acceptable and mark the decision decided_by: user."
|
|
50014
|
+
prompt: "The cited raw evidence includes URL(s), but this document/link/reference claim does not preserve them in proposed content or detail. " + "Add the relevant URL(s) to proposed.detail and rerun review, or ask the user to confirm that omitting the link is acceptable and mark the decision decided_by: user. Auto mode or general permission to continue is not confirmation."
|
|
49897
50015
|
};
|
|
49898
50016
|
}
|
|
49899
50017
|
function urlReferencePreservationQuestions(input) {
|
|
@@ -50103,7 +50221,7 @@ function semanticDecisionSchemaErrorReview(input) {
|
|
|
50103
50221
|
}
|
|
50104
50222
|
|
|
50105
50223
|
// src/reconcile/review.ts
|
|
50106
|
-
var
|
|
50224
|
+
var FENCED_CODE_RE2 = /(^|\n)\s*(```|~~~)/u;
|
|
50107
50225
|
function isRecord21(value) {
|
|
50108
50226
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50109
50227
|
}
|
|
@@ -50371,6 +50489,11 @@ function actionNeedsSourceSupport2(action) {
|
|
|
50371
50489
|
function actionRequiresStrictSourceSupport(action) {
|
|
50372
50490
|
return action === "merge_update" || action === "supersede" || action === "reanchor" || action === "split_then_reanchor";
|
|
50373
50491
|
}
|
|
50492
|
+
function sourceRefChangedFromPreparedSupport(input) {
|
|
50493
|
+
const proposedSourceRef = input.decision.proposed?.source_ref;
|
|
50494
|
+
const preparedSourceRef = input.item?.source_support?.source_ref;
|
|
50495
|
+
return typeof proposedSourceRef === "string" && typeof preparedSourceRef === "string" && proposedSourceRef !== preparedSourceRef;
|
|
50496
|
+
}
|
|
50374
50497
|
function supportKind(decision, item) {
|
|
50375
50498
|
if (typeof decision.proposed?.kind === "string")
|
|
50376
50499
|
return decision.proposed.kind;
|
|
@@ -50396,21 +50519,14 @@ function sourceSupportIssue(input) {
|
|
|
50396
50519
|
...splitCandidateCount > 1 ? { code: "source-support-split-by-evidence-blocks" } : {}
|
|
50397
50520
|
};
|
|
50398
50521
|
}
|
|
50399
|
-
function
|
|
50400
|
-
const sourceAlias2 = typeof input.decision.proposed?.source_ref === "string" ? /^(src-\d+)#/u.exec(input.decision.proposed.source_ref)?.[1] : undefined;
|
|
50401
|
-
const node2 = input.decision.target?.node ?? input.item?.target?.node;
|
|
50402
|
-
const groupKey = node2 !== undefined && sourceAlias2 !== undefined ? `support:${node2}:${sourceAlias2}` : undefined;
|
|
50522
|
+
function weakSupportWarning(input) {
|
|
50403
50523
|
return {
|
|
50404
|
-
|
|
50524
|
+
path: `${input.decision.item_id}.proposed.source_ref`,
|
|
50405
50525
|
item_id: input.decision.item_id,
|
|
50406
|
-
|
|
50407
|
-
|
|
50408
|
-
|
|
50409
|
-
|
|
50410
|
-
} : {},
|
|
50411
|
-
...input.summary !== undefined ? { candidate_summary: { active: input.summary.active, archive: input.summary.archive } } : {},
|
|
50412
|
-
...input.decision.proposed?.content !== undefined ? { proposed_content: input.decision.proposed.content } : {},
|
|
50413
|
-
prompt: "The proposed new knowledge is only weakly supported by the cited raw text. " + `${formatSupportDiagnostic(input.diagnostic)}. ` + "Ask the user whether this is an acceptable summary/compression; if confirmed, regenerate the final decision with decided_by: user."
|
|
50526
|
+
severity: "warning",
|
|
50527
|
+
code: "weak-source-support",
|
|
50528
|
+
message: `proposed keep_separate is weakly supported by its cited raw text: ${formatSupportDiagnostic(input.diagnostic)}.`,
|
|
50529
|
+
next_action: "Review the cited evidence if the summary looks surprising; missing hard facts still remain errors."
|
|
50414
50530
|
};
|
|
50415
50531
|
}
|
|
50416
50532
|
function citedTextForDecision2(decision, item) {
|
|
@@ -50431,9 +50547,9 @@ function needsExampleDetailPreservationQuestion(input) {
|
|
|
50431
50547
|
return false;
|
|
50432
50548
|
if (supportKind(input.decision, input.item) !== "example")
|
|
50433
50549
|
return false;
|
|
50434
|
-
if (!
|
|
50550
|
+
if (!FENCED_CODE_RE2.test(citedTextForDecision2(input.decision, input.item)))
|
|
50435
50551
|
return false;
|
|
50436
|
-
return !
|
|
50552
|
+
return !FENCED_CODE_RE2.test(input.decision.proposed?.detail ?? "");
|
|
50437
50553
|
}
|
|
50438
50554
|
function exampleDetailPreservationQuestion(input) {
|
|
50439
50555
|
return {
|
|
@@ -50442,7 +50558,7 @@ function exampleDetailPreservationQuestion(input) {
|
|
|
50442
50558
|
type: "example_detail_preservation",
|
|
50443
50559
|
...input.summary !== undefined ? { candidate_summary: { active: input.summary.active, archive: input.summary.archive } } : {},
|
|
50444
50560
|
...input.decision.proposed?.content !== undefined ? { proposed_content: input.decision.proposed.content } : {},
|
|
50445
|
-
prompt: "The cited raw evidence contains a fenced code/config/command block, but this example decision does not preserve it in proposed.detail. " + "Add the relevant fenced block to proposed.detail and rerun review, or ask the user to confirm that a prose-only summary is acceptable and mark the decision decided_by: user."
|
|
50561
|
+
prompt: "The cited raw evidence contains a fenced code/config/command block, but this example decision does not preserve it in proposed.detail. " + "Add the relevant fenced block to proposed.detail and rerun review, or ask the user to confirm that a prose-only summary is acceptable and mark the decision decided_by: user. Auto mode or general permission to continue is not confirmation."
|
|
50446
50562
|
};
|
|
50447
50563
|
}
|
|
50448
50564
|
function itemRequiresDecision(item) {
|
|
@@ -50515,15 +50631,13 @@ function reviewSemanticDecisions(input) {
|
|
|
50515
50631
|
}
|
|
50516
50632
|
for (const prior of priorResults) {
|
|
50517
50633
|
const diagnostic = supportDiagnosticFromItem({ decision: prior.decision, item: prior.item });
|
|
50518
|
-
|
|
50519
|
-
|
|
50634
|
+
const changedSourceRef = sourceRefChangedFromPreparedSupport({ decision: prior.decision, item: prior.item });
|
|
50635
|
+
if (diagnostic?.verdict === "weak" && prior.decision.action === "keep_separate" && !changedSourceRef) {
|
|
50636
|
+
issues.push(weakSupportWarning({
|
|
50520
50637
|
decision: prior.decision,
|
|
50521
|
-
item: prior.item,
|
|
50522
|
-
questionIndex: questions.length,
|
|
50523
|
-
summary: candidateSummaryByItem.get(prior.decision.item_id),
|
|
50524
50638
|
diagnostic
|
|
50525
50639
|
}));
|
|
50526
|
-
} else if (diagnostic?.verdict === "unsupported" || diagnostic?.verdict === "weak" && !(prior.decision.action === "keep_separate" && prior.decision.decided_by === "user")) {
|
|
50640
|
+
} else if (diagnostic?.verdict === "unsupported" || changedSourceRef && diagnostic?.verdict === "weak" || diagnostic?.verdict === "weak" && !(prior.decision.action === "keep_separate" && prior.decision.decided_by === "user")) {
|
|
50527
50641
|
issues.push(sourceSupportIssue({ decision: prior.decision, diagnostic, support: prior.item?.source_support }));
|
|
50528
50642
|
}
|
|
50529
50643
|
}
|
|
@@ -50651,6 +50765,7 @@ async function reviewSemanticDecisionsWithWorkspace(input) {
|
|
|
50651
50765
|
if (node2 === undefined)
|
|
50652
50766
|
continue;
|
|
50653
50767
|
try {
|
|
50768
|
+
const changedPreparedSourceRef = typeof item?.source_support?.source_ref === "string" && item.source_support.source_ref !== sourceRef;
|
|
50654
50769
|
const result = await diagnoseSectionSourceSupport({
|
|
50655
50770
|
ctxDir: input.ctxDir,
|
|
50656
50771
|
nodeSources: node2.parsed.node.sources,
|
|
@@ -50663,13 +50778,21 @@ async function reviewSemanticDecisionsWithWorkspace(input) {
|
|
|
50663
50778
|
source_ref: sourceRef
|
|
50664
50779
|
}
|
|
50665
50780
|
});
|
|
50666
|
-
|
|
50781
|
+
const diagnostic = changedPreparedSourceRef && result.diagnostic.verdict === "weak" ? { ...result.diagnostic, verdict: "unsupported" } : result.diagnostic;
|
|
50782
|
+
const sourceSupport = sourceSupportFromDiagnostic({
|
|
50667
50783
|
sourceRef: result.sourceRef,
|
|
50668
50784
|
citedText: result.citedText,
|
|
50669
|
-
diagnostic
|
|
50785
|
+
diagnostic,
|
|
50670
50786
|
...result.evidenceBlock !== undefined ? { evidenceBlock: result.evidenceBlock } : {},
|
|
50671
50787
|
...result.evidenceBlockCandidates !== undefined ? { evidenceBlockCandidates: result.evidenceBlockCandidates } : {}
|
|
50672
50788
|
});
|
|
50789
|
+
if (changedPreparedSourceRef && result.diagnostic.verdict === "weak") {
|
|
50790
|
+
const withoutCitedText = { ...sourceSupport };
|
|
50791
|
+
delete withoutCitedText.cited_text;
|
|
50792
|
+
item.source_support = withoutCitedText;
|
|
50793
|
+
} else {
|
|
50794
|
+
item.source_support = sourceSupport;
|
|
50795
|
+
}
|
|
50673
50796
|
} catch (error) {
|
|
50674
50797
|
item.source_support = {
|
|
50675
50798
|
source_ref: sourceRef,
|
|
@@ -50755,10 +50878,10 @@ init_compileNode();
|
|
|
50755
50878
|
init_coverage();
|
|
50756
50879
|
init_evidence();
|
|
50757
50880
|
init_ledger();
|
|
50758
|
-
import { existsSync as
|
|
50881
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
50759
50882
|
import { cp as cp3, mkdir as mkdir25, mkdtemp as mkdtemp2, rm as rm14 } from "node:fs/promises";
|
|
50760
50883
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
50761
|
-
import { dirname as dirname22, join as
|
|
50884
|
+
import { dirname as dirname22, join as join55 } from "node:path";
|
|
50762
50885
|
init_sourceSupport();
|
|
50763
50886
|
init_locatedNodeSources();
|
|
50764
50887
|
init_types3();
|
|
@@ -50848,13 +50971,13 @@ async function propagateOmitCoverageSkips(input) {
|
|
|
50848
50971
|
}
|
|
50849
50972
|
}
|
|
50850
50973
|
async function createApplyRollbackSnapshot(ctxDir) {
|
|
50851
|
-
const tmpDir = await mkdtemp2(
|
|
50852
|
-
const knowledgePath2 =
|
|
50974
|
+
const tmpDir = await mkdtemp2(join55(tmpdir2(), "c4a-reconcile-apply-"));
|
|
50975
|
+
const knowledgePath2 = join55(ctxDir, "knowledge");
|
|
50853
50976
|
const ledgerPath = semanticLedgerPath2(ctxDir);
|
|
50854
|
-
const knowledgeBackup =
|
|
50855
|
-
const ledgerBackup =
|
|
50856
|
-
const knowledgeExisted =
|
|
50857
|
-
const ledgerExisted =
|
|
50977
|
+
const knowledgeBackup = join55(tmpDir, "knowledge");
|
|
50978
|
+
const ledgerBackup = join55(tmpDir, "semantic.yaml");
|
|
50979
|
+
const knowledgeExisted = existsSync46(knowledgePath2);
|
|
50980
|
+
const ledgerExisted = existsSync46(ledgerPath);
|
|
50858
50981
|
if (knowledgeExisted)
|
|
50859
50982
|
await cp3(knowledgePath2, knowledgeBackup, { recursive: true });
|
|
50860
50983
|
if (ledgerExisted)
|
|
@@ -50862,7 +50985,7 @@ async function createApplyRollbackSnapshot(ctxDir) {
|
|
|
50862
50985
|
return { ctxDir, tmpDir, knowledgeExisted, ledgerExisted, knowledgeBackup, ledgerBackup };
|
|
50863
50986
|
}
|
|
50864
50987
|
async function restoreApplyRollbackSnapshot(snapshot) {
|
|
50865
|
-
const knowledgePath2 =
|
|
50988
|
+
const knowledgePath2 = join55(snapshot.ctxDir, "knowledge");
|
|
50866
50989
|
if (snapshot.knowledgeExisted) {
|
|
50867
50990
|
await rm14(knowledgePath2, { recursive: true, force: true });
|
|
50868
50991
|
await cp3(snapshot.knowledgeBackup, knowledgePath2, { recursive: true });
|
|
@@ -52364,9 +52487,9 @@ init_edge2();
|
|
|
52364
52487
|
// src/lib/glossary.ts
|
|
52365
52488
|
init_knowledge();
|
|
52366
52489
|
init_nodeParser();
|
|
52367
|
-
import { existsSync as
|
|
52490
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
52368
52491
|
import { readdir as readdir21, readFile as readFile55 } from "node:fs/promises";
|
|
52369
|
-
import { join as
|
|
52492
|
+
import { join as join56 } from "node:path";
|
|
52370
52493
|
function collectGlossaryFromParsedTree(parsed, glossary, allowedTypes) {
|
|
52371
52494
|
const type = parsed.node.type;
|
|
52372
52495
|
if (allowedTypes.has(type)) {
|
|
@@ -52389,16 +52512,16 @@ function normalizeGlossaryTerm(value) {
|
|
|
52389
52512
|
return value.normalize("NFC").trim().toLowerCase();
|
|
52390
52513
|
}
|
|
52391
52514
|
function resolveKnowledgeRoot(path8) {
|
|
52392
|
-
if (
|
|
52393
|
-
return
|
|
52515
|
+
if (existsSync47(join56(path8, "knowledge"))) {
|
|
52516
|
+
return join56(path8, "knowledge");
|
|
52394
52517
|
}
|
|
52395
52518
|
return path8;
|
|
52396
52519
|
}
|
|
52397
52520
|
async function readGlossaryFiles(dir) {
|
|
52398
|
-
if (!
|
|
52521
|
+
if (!existsSync47(dir))
|
|
52399
52522
|
return [];
|
|
52400
52523
|
const entries = await readdir21(dir, { withFileTypes: true });
|
|
52401
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "_index.md" && entry.name !== "changelog.md").map((entry) =>
|
|
52524
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "_index.md" && entry.name !== "changelog.md").map((entry) => join56(dir, entry.name));
|
|
52402
52525
|
}
|
|
52403
52526
|
async function buildGlossary(path8, options = {}) {
|
|
52404
52527
|
const knowledgeRoot2 = resolveKnowledgeRoot(path8);
|
|
@@ -52406,7 +52529,7 @@ async function buildGlossary(path8, options = {}) {
|
|
|
52406
52529
|
const allowedTypes = new Set(targets);
|
|
52407
52530
|
const glossary = [];
|
|
52408
52531
|
for (const type of targets) {
|
|
52409
|
-
const files = await readGlossaryFiles(
|
|
52532
|
+
const files = await readGlossaryFiles(join56(knowledgeRoot2, type));
|
|
52410
52533
|
for (const file of files) {
|
|
52411
52534
|
const parsed = parseNodeMarkdown(await readFile55(file, "utf8"));
|
|
52412
52535
|
if (parsed.node.type !== type)
|
|
@@ -53020,9 +53143,9 @@ function registerMdriveCommands(program2) {
|
|
|
53020
53143
|
init_cliFeedback();
|
|
53021
53144
|
init_errors();
|
|
53022
53145
|
var import_yaml32 = __toESM(require_dist(), 1);
|
|
53023
|
-
import { existsSync as
|
|
53146
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
53024
53147
|
import { lstat as lstat2, mkdir as mkdir28, readFile as readFile58, readdir as readdir22, realpath as realpath3, rename as rename7, writeFile as writeFile21 } from "node:fs/promises";
|
|
53025
|
-
import { dirname as dirname25, isAbsolute as isAbsolute6, join as
|
|
53148
|
+
import { dirname as dirname25, isAbsolute as isAbsolute6, join as join57, relative as relative12 } from "node:path";
|
|
53026
53149
|
init_workspaceLayout();
|
|
53027
53150
|
init_section();
|
|
53028
53151
|
init_exitCode();
|
|
@@ -53086,7 +53209,7 @@ async function assertRealPathInside(ctxDir, targetPath) {
|
|
|
53086
53209
|
});
|
|
53087
53210
|
}
|
|
53088
53211
|
function resolveWorkspacePath(ctxDir, relPath) {
|
|
53089
|
-
return relPath === "." ? ctxDir :
|
|
53212
|
+
return relPath === "." ? ctxDir : join57(ctxDir, relPath);
|
|
53090
53213
|
}
|
|
53091
53214
|
function pathKind2(stats) {
|
|
53092
53215
|
if (stats.isFile())
|
|
@@ -53241,7 +53364,7 @@ function assertWorkspaceWriteAllowed(relPath) {
|
|
|
53241
53364
|
async function writeWorkspaceFile(input) {
|
|
53242
53365
|
assertWorkspaceWriteAllowed(input.relPath);
|
|
53243
53366
|
const destination = resolveWorkspacePath(input.ctxDir, input.relPath);
|
|
53244
|
-
if (
|
|
53367
|
+
if (existsSync48(destination) && !input.overwrite) {
|
|
53245
53368
|
throw new ContextError(ExitCode.UserError, `workspace file already exists: ${input.relPath}`, {
|
|
53246
53369
|
category: ErrorCategory.UserInputInvalid,
|
|
53247
53370
|
path: input.relPath
|
|
@@ -54122,12 +54245,32 @@ function candidateRows(value) {
|
|
|
54122
54245
|
churn: candidate.churn
|
|
54123
54246
|
}));
|
|
54124
54247
|
}
|
|
54248
|
+
function sourceReadingRows(value) {
|
|
54249
|
+
if (!isRecord24(value))
|
|
54250
|
+
return [];
|
|
54251
|
+
return asRecords(value.source_readings).map((reading) => ({
|
|
54252
|
+
...typeof reading.source_id === "string" ? { source_id: reading.source_id } : {},
|
|
54253
|
+
...typeof reading.segments_digest === "string" ? { segments_digest: reading.segments_digest } : {},
|
|
54254
|
+
...typeof reading.coarse_read_digest === "string" ? { coarse_read_digest: reading.coarse_read_digest } : {},
|
|
54255
|
+
...typeof reading.read_at === "string" ? { read_at: reading.read_at } : {},
|
|
54256
|
+
...typeof reading.density_profile === "string" ? { density_profile: reading.density_profile } : {},
|
|
54257
|
+
reading_anchor_count: asRecords(reading.reading_anchors).length,
|
|
54258
|
+
section_proposal_count: asRecords(reading.section_proposals).length,
|
|
54259
|
+
reading_anchors: reading.reading_anchors,
|
|
54260
|
+
section_proposals: reading.section_proposals
|
|
54261
|
+
}));
|
|
54262
|
+
}
|
|
54125
54263
|
function compactAlignLedger(record, value, options) {
|
|
54126
54264
|
if (!isRecord24(value))
|
|
54127
54265
|
return { candidates: [], block_dispositions: [], counts: { candidates: 0 } };
|
|
54128
54266
|
const allCandidates = candidateRows(value);
|
|
54267
|
+
const allSourceReadings = sourceReadingRows(value);
|
|
54268
|
+
const sourceReadings = allSourceReadings.filter((reading) => options.sourceId === undefined || reading.source_id === options.sourceId);
|
|
54129
54269
|
const filtered = allCandidates.filter((candidate) => (options.status === undefined || candidate.status === options.status) && (options.candidateId === undefined || candidate.candidate_id === options.candidateId));
|
|
54130
|
-
const
|
|
54270
|
+
const shouldShowCandidates = options.sourceId === undefined || options.status !== undefined || options.candidateId !== undefined;
|
|
54271
|
+
const candidateRowsForDetail = shouldShowCandidates ? filtered : [];
|
|
54272
|
+
const sourceReadingsForDetail = options.sourceId === undefined ? [] : sourceReadings;
|
|
54273
|
+
const wantsDetail = options.sourceId !== undefined || options.status !== undefined || options.candidateId !== undefined || options.pageSize !== undefined || options.pageToken !== undefined;
|
|
54131
54274
|
if (!wantsDetail) {
|
|
54132
54275
|
const firstCandidateId = allCandidates.find((candidate) => candidate.candidate_id !== undefined)?.candidate_id;
|
|
54133
54276
|
const firstStatus = allCandidates.find((candidate) => candidate.status !== undefined)?.status;
|
|
@@ -54136,10 +54279,18 @@ function compactAlignLedger(record, value, options) {
|
|
|
54136
54279
|
last_batch_id: value.last_batch_id,
|
|
54137
54280
|
summary: {
|
|
54138
54281
|
candidates: allCandidates.length,
|
|
54282
|
+
source_readings: allSourceReadings.length,
|
|
54139
54283
|
block_dispositions: asRecords(value.block_dispositions).length,
|
|
54140
54284
|
by_status: countBy(allCandidates.map((candidate) => candidate.status ?? "unknown")),
|
|
54141
54285
|
by_node_type: countBy(allCandidates.map((candidate) => candidate.node_type ?? "unknown"))
|
|
54142
54286
|
},
|
|
54287
|
+
source_reading_index: allSourceReadings.slice(0, DEFAULT_INDEX_LIMIT).map((reading) => ({
|
|
54288
|
+
source_id: reading.source_id,
|
|
54289
|
+
density_profile: reading.density_profile,
|
|
54290
|
+
reading_anchor_count: reading.reading_anchor_count,
|
|
54291
|
+
section_proposal_count: reading.section_proposal_count,
|
|
54292
|
+
read_at: reading.read_at
|
|
54293
|
+
})),
|
|
54143
54294
|
candidate_index: allCandidates.slice(0, DEFAULT_INDEX_LIMIT).map((candidate) => ({
|
|
54144
54295
|
candidate_id: candidate.candidate_id,
|
|
54145
54296
|
current_slug: candidate.current_slug,
|
|
@@ -54162,11 +54313,19 @@ function compactAlignLedger(record, value, options) {
|
|
|
54162
54313
|
}]
|
|
54163
54314
|
};
|
|
54164
54315
|
}
|
|
54165
|
-
const page = paginate(
|
|
54316
|
+
const page = paginate(candidateRowsForDetail, options);
|
|
54166
54317
|
return {
|
|
54167
54318
|
mode: "detail",
|
|
54168
54319
|
last_batch_id: value.last_batch_id,
|
|
54169
54320
|
filters: compactFilters(options),
|
|
54321
|
+
source_readings: sourceReadingsForDetail,
|
|
54322
|
+
...options.sourceId === undefined ? { source_reading_index: allSourceReadings.slice(0, DEFAULT_INDEX_LIMIT).map((reading) => ({
|
|
54323
|
+
source_id: reading.source_id,
|
|
54324
|
+
density_profile: reading.density_profile,
|
|
54325
|
+
reading_anchor_count: reading.reading_anchor_count,
|
|
54326
|
+
section_proposal_count: reading.section_proposal_count,
|
|
54327
|
+
read_at: reading.read_at
|
|
54328
|
+
})) } : {},
|
|
54170
54329
|
candidates: page.items,
|
|
54171
54330
|
block_dispositions: asRecords(value.block_dispositions).map((item) => ({
|
|
54172
54331
|
block_id: item.block_id,
|
|
@@ -54177,9 +54336,18 @@ function compactAlignLedger(record, value, options) {
|
|
|
54177
54336
|
...page.page !== undefined ? { page: page.page } : {},
|
|
54178
54337
|
counts: {
|
|
54179
54338
|
matched_candidates: filtered.length,
|
|
54339
|
+
candidate_filter_applied: shouldShowCandidates,
|
|
54180
54340
|
shown_candidates: page.items.length,
|
|
54341
|
+
matched_source_readings: sourceReadings.length,
|
|
54342
|
+
shown_source_readings: sourceReadingsForDetail.length,
|
|
54181
54343
|
block_dispositions: asRecords(value.block_dispositions).length
|
|
54182
|
-
}
|
|
54344
|
+
},
|
|
54345
|
+
...options.sourceId !== undefined && !shouldShowCandidates ? { agent_hints: [{
|
|
54346
|
+
code: "workflow-ledger-source-filter",
|
|
54347
|
+
severity: "info",
|
|
54348
|
+
message: "--source filters source_readings only; candidates do not carry source_id.",
|
|
54349
|
+
next_action: "Add --status or --candidate-id when you also need candidate rows from the ledger view."
|
|
54350
|
+
}] } : {}
|
|
54183
54351
|
};
|
|
54184
54352
|
}
|
|
54185
54353
|
function compactAlignAggregate(record, value, options) {
|
|
@@ -55514,6 +55682,16 @@ function coverageSkipPatch(input) {
|
|
|
55514
55682
|
}]
|
|
55515
55683
|
};
|
|
55516
55684
|
}
|
|
55685
|
+
function coverageSkipUnresolvedPatch(input) {
|
|
55686
|
+
return {
|
|
55687
|
+
schema_version: COVERAGE_DISPOSITION_SCHEMA_VERSION,
|
|
55688
|
+
dispositions: input.candidates.filter((candidate) => candidate.status === "unresolved").map((candidate) => ({
|
|
55689
|
+
candidate_id: candidate.candidate_id,
|
|
55690
|
+
action: "skip",
|
|
55691
|
+
reason: input.reason
|
|
55692
|
+
}))
|
|
55693
|
+
};
|
|
55694
|
+
}
|
|
55517
55695
|
function renderCompileClosedFeedbackBody(input) {
|
|
55518
55696
|
const lines = [];
|
|
55519
55697
|
if (input.workflowSummary)
|
|
@@ -56191,11 +56369,10 @@ function candidateId(batchId, localRef, slug) {
|
|
|
56191
56369
|
const hash2 = createHash8("sha256").update(`${batchId}\x00${localRef}\x00${slug}`).digest("hex").slice(0, 16);
|
|
56192
56370
|
return `cand_${hash2}`;
|
|
56193
56371
|
}
|
|
56194
|
-
function
|
|
56195
|
-
return
|
|
56196
|
-
|
|
56197
|
-
|
|
56198
|
-
id_mappings_from_last_batch: [],
|
|
56372
|
+
function cloneLedgerState(ledger) {
|
|
56373
|
+
return {
|
|
56374
|
+
schema_version: ledger.schema_version,
|
|
56375
|
+
source_readings: cloneSourceReadings(ledger.source_readings),
|
|
56199
56376
|
candidates: ledger.candidates.map((candidate) => ({
|
|
56200
56377
|
...candidate,
|
|
56201
56378
|
block_ids: [...candidate.block_ids],
|
|
@@ -56210,10 +56387,14 @@ function cloneLedger(ledger, batchId) {
|
|
|
56210
56387
|
proposed_sections: ledger.proposed_sections.map((item) => ({ ...item })),
|
|
56211
56388
|
ops_history: [...ledger.ops_history],
|
|
56212
56389
|
warnings: [...ledger.warnings]
|
|
56213
|
-
}
|
|
56390
|
+
};
|
|
56391
|
+
}
|
|
56392
|
+
function emptyLedger2(batchId) {
|
|
56393
|
+
return {
|
|
56214
56394
|
schema_version: ALIGN_CANDIDATE_LEDGER_SCHEMA_VERSION,
|
|
56215
56395
|
last_batch_id: batchId,
|
|
56216
56396
|
id_mappings_from_last_batch: [],
|
|
56397
|
+
source_readings: [],
|
|
56217
56398
|
candidates: [],
|
|
56218
56399
|
block_dispositions: [],
|
|
56219
56400
|
parent_hints: [],
|
|
@@ -56223,6 +56404,55 @@ function cloneLedger(ledger, batchId) {
|
|
|
56223
56404
|
warnings: []
|
|
56224
56405
|
};
|
|
56225
56406
|
}
|
|
56407
|
+
function cloneLedger(ledger, batchId) {
|
|
56408
|
+
if (ledger === undefined)
|
|
56409
|
+
return emptyLedger2(batchId);
|
|
56410
|
+
return {
|
|
56411
|
+
...cloneLedgerState(ledger),
|
|
56412
|
+
last_batch_id: batchId,
|
|
56413
|
+
id_mappings_from_last_batch: []
|
|
56414
|
+
};
|
|
56415
|
+
}
|
|
56416
|
+
function cloneSourceReadings(value) {
|
|
56417
|
+
return (value ?? []).map((reading) => ({
|
|
56418
|
+
...reading,
|
|
56419
|
+
reading_anchors: reading.reading_anchors.map((anchor) => ({ ...anchor })),
|
|
56420
|
+
section_proposals: reading.section_proposals.map((section) => ({ ...section }))
|
|
56421
|
+
}));
|
|
56422
|
+
}
|
|
56423
|
+
function cloneLedgerForSourceReading(ledger) {
|
|
56424
|
+
if (ledger === undefined)
|
|
56425
|
+
return emptyLedger2("coarse-read");
|
|
56426
|
+
return {
|
|
56427
|
+
...cloneLedgerState(ledger),
|
|
56428
|
+
last_batch_id: ledger.last_batch_id,
|
|
56429
|
+
id_mappings_from_last_batch: ledger.id_mappings_from_last_batch.map((mapping) => ({ ...mapping }))
|
|
56430
|
+
};
|
|
56431
|
+
}
|
|
56432
|
+
function upsertSourceReading(input) {
|
|
56433
|
+
const ledger = cloneLedgerForSourceReading(input.ledger);
|
|
56434
|
+
const densityProfile = text(input.coarseRead.density_profile);
|
|
56435
|
+
if (densityProfile === undefined) {
|
|
56436
|
+
throw new ContextError(ExitCode.UserError, "density_profile is required before storing source reading", {
|
|
56437
|
+
category: ErrorCategory.SchemaInvalid,
|
|
56438
|
+
path: "density_profile"
|
|
56439
|
+
});
|
|
56440
|
+
}
|
|
56441
|
+
const next = {
|
|
56442
|
+
source_id: input.sourceId,
|
|
56443
|
+
segments_digest: input.segmentsDigest,
|
|
56444
|
+
coarse_read_digest: input.coarseReadDigest,
|
|
56445
|
+
read_at: (input.now ?? new Date).toISOString(),
|
|
56446
|
+
density_profile: densityProfile,
|
|
56447
|
+
reading_anchors: Array.isArray(input.coarseRead.reading_anchors) ? input.coarseRead.reading_anchors.filter(isRecord26).map((anchor) => ({ ...anchor })) : [],
|
|
56448
|
+
section_proposals: Array.isArray(input.coarseRead.section_proposals) ? input.coarseRead.section_proposals.filter(isRecord26).map((section) => ({ ...section })) : []
|
|
56449
|
+
};
|
|
56450
|
+
ledger.source_readings = [
|
|
56451
|
+
...(ledger.source_readings ?? []).filter((reading) => reading.source_id !== input.sourceId),
|
|
56452
|
+
next
|
|
56453
|
+
].sort((left, right) => left.source_id.localeCompare(right.source_id));
|
|
56454
|
+
return ledger;
|
|
56455
|
+
}
|
|
56226
56456
|
function terminal(candidate) {
|
|
56227
56457
|
return candidate?.status === "rejected" || candidate?.status === "superseded";
|
|
56228
56458
|
}
|
|
@@ -56754,6 +56984,65 @@ function nodesFromDecision(decision, sources, language) {
|
|
|
56754
56984
|
};
|
|
56755
56985
|
});
|
|
56756
56986
|
}
|
|
56987
|
+
function nodeWithRecomputedSources(node2, sources) {
|
|
56988
|
+
const sourceIds = sourceIdsForNode(sources, node2.slug);
|
|
56989
|
+
return {
|
|
56990
|
+
slug: node2.slug,
|
|
56991
|
+
type: node2.type,
|
|
56992
|
+
tags: node2.tags,
|
|
56993
|
+
title: node2.title,
|
|
56994
|
+
sources: sourceIds.evidence,
|
|
56995
|
+
...sourceIds.context.length > 0 ? { context_sources: sourceIds.context } : {},
|
|
56996
|
+
...node2.summary !== undefined ? { summary: node2.summary } : {},
|
|
56997
|
+
...node2.language !== undefined ? { language: node2.language } : {},
|
|
56998
|
+
...node2.contains_parent !== undefined ? { contains_parent: node2.contains_parent } : {},
|
|
56999
|
+
...node2.planned_sections !== undefined ? { planned_sections: node2.planned_sections } : {},
|
|
57000
|
+
...node2.domain_gate !== undefined ? { domain_gate: node2.domain_gate } : {},
|
|
57001
|
+
...node2.action_gate !== undefined ? { action_gate: node2.action_gate } : {}
|
|
57002
|
+
};
|
|
57003
|
+
}
|
|
57004
|
+
function mergeSources(input) {
|
|
57005
|
+
const currentBySource = new Map(input.current.map((source2) => [source2.source_id, source2]));
|
|
57006
|
+
const seen = new Set;
|
|
57007
|
+
const merged = input.previous.map((source2) => {
|
|
57008
|
+
const replacement = currentBySource.get(source2.source_id);
|
|
57009
|
+
if (replacement !== undefined) {
|
|
57010
|
+
seen.add(source2.source_id);
|
|
57011
|
+
return replacement;
|
|
57012
|
+
}
|
|
57013
|
+
return source2;
|
|
57014
|
+
});
|
|
57015
|
+
for (const source2 of input.current) {
|
|
57016
|
+
if (!seen.has(source2.source_id))
|
|
57017
|
+
merged.push(source2);
|
|
57018
|
+
}
|
|
57019
|
+
return merged;
|
|
57020
|
+
}
|
|
57021
|
+
function mergeNodes(input) {
|
|
57022
|
+
const currentBySlug = new Map(input.current.map((node2) => [node2.slug, node2]));
|
|
57023
|
+
const seen = new Set;
|
|
57024
|
+
const merged = input.previous.map((node2) => {
|
|
57025
|
+
const replacement = currentBySlug.get(node2.slug);
|
|
57026
|
+
seen.add(node2.slug);
|
|
57027
|
+
return nodeWithRecomputedSources(replacement ?? node2, input.sources);
|
|
57028
|
+
});
|
|
57029
|
+
for (const node2 of input.current) {
|
|
57030
|
+
if (!seen.has(node2.slug))
|
|
57031
|
+
merged.push(nodeWithRecomputedSources(node2, input.sources));
|
|
57032
|
+
}
|
|
57033
|
+
return merged;
|
|
57034
|
+
}
|
|
57035
|
+
function mergeEdges(input) {
|
|
57036
|
+
const byKey = new Map;
|
|
57037
|
+
const put = (edge2) => {
|
|
57038
|
+
if (!input.activeSlugs.has(edge2.from) || !input.activeSlugs.has(edge2.to))
|
|
57039
|
+
return;
|
|
57040
|
+
byKey.set(`${edge2.type}\x00${edge2.from}\x00${edge2.to}`, edge2);
|
|
57041
|
+
};
|
|
57042
|
+
input.previous.forEach(put);
|
|
57043
|
+
input.current.forEach(put);
|
|
57044
|
+
return [...byKey.values()].sort((left, right) => `${left.from}\x00${left.to}`.localeCompare(`${right.from}\x00${right.to}`));
|
|
57045
|
+
}
|
|
56757
57046
|
function isCitationEligibleForNode(block, slug) {
|
|
56758
57047
|
if (block.status === "owned")
|
|
56759
57048
|
return block.owner === slug || block.coverable_by.includes(slug);
|
|
@@ -56769,6 +57058,16 @@ function isVisibleNonCitationForNode(block, slug) {
|
|
|
56769
57058
|
function isContainerDomain(nodes, node2) {
|
|
56770
57059
|
return node2.type === "domain" && nodes.some((candidate) => candidate.contains_parent === node2.slug);
|
|
56771
57060
|
}
|
|
57061
|
+
function isExplicitNoWriteNode2(node2) {
|
|
57062
|
+
return node2.planned_sections !== undefined && node2.planned_sections.length === 0;
|
|
57063
|
+
}
|
|
57064
|
+
function hasFinalizedGraphLink2(ownership, node2) {
|
|
57065
|
+
if (typeof node2.contains_parent === "string" && node2.contains_parent.length > 0)
|
|
57066
|
+
return true;
|
|
57067
|
+
if ((ownership.nodes ?? []).some((candidate) => candidate.contains_parent === node2.slug))
|
|
57068
|
+
return true;
|
|
57069
|
+
return (ownership.edges ?? []).some((edge2) => edge2.from === node2.slug || edge2.to === node2.slug);
|
|
57070
|
+
}
|
|
56772
57071
|
function auditFinalizedCitationEvidence(ownership) {
|
|
56773
57072
|
const nodes = ownership.nodes ?? [];
|
|
56774
57073
|
const blocks = ownership.sources.flatMap((source2) => source2.blocks);
|
|
@@ -56778,6 +57077,9 @@ function auditFinalizedCitationEvidence(ownership) {
|
|
|
56778
57077
|
if (blocks.some((block) => isCitationEligibleForNode(block, node2.slug)))
|
|
56779
57078
|
return [];
|
|
56780
57079
|
const visibleBlocks = blocks.filter((block) => isVisibleNonCitationForNode(block, node2.slug)).map((block) => block.block_id).filter((blockId2) => typeof blockId2 === "string" && blockId2.length > 0);
|
|
57080
|
+
if (isExplicitNoWriteNode2(node2) && (visibleBlocks.length > 0 || hasFinalizedGraphLink2(ownership, node2))) {
|
|
57081
|
+
return [];
|
|
57082
|
+
}
|
|
56781
57083
|
return [{
|
|
56782
57084
|
kind: "node_without_citation_evidence",
|
|
56783
57085
|
node_slug: node2.slug,
|
|
@@ -56835,6 +57137,31 @@ function buildSourceOwnershipFromFinalized(input) {
|
|
|
56835
57137
|
summary: summary(sources)
|
|
56836
57138
|
};
|
|
56837
57139
|
}
|
|
57140
|
+
function mergeIncrementalSourceOwnership(input) {
|
|
57141
|
+
const sources = mergeSources({
|
|
57142
|
+
previous: input.previous.sources,
|
|
57143
|
+
current: input.current.sources
|
|
57144
|
+
});
|
|
57145
|
+
const nodes = mergeNodes({
|
|
57146
|
+
previous: input.previous.nodes ?? [],
|
|
57147
|
+
current: input.current.nodes ?? [],
|
|
57148
|
+
sources
|
|
57149
|
+
});
|
|
57150
|
+
const activeSlugs = new Set(nodes.map((node2) => node2.slug));
|
|
57151
|
+
const edges = mergeEdges({
|
|
57152
|
+
previous: input.previous.edges ?? [],
|
|
57153
|
+
current: input.current.edges ?? [],
|
|
57154
|
+
activeSlugs
|
|
57155
|
+
});
|
|
57156
|
+
return {
|
|
57157
|
+
schema_version: SOURCE_OWNERSHIP_SCHEMA_VERSION,
|
|
57158
|
+
generated_at: input.current.generated_at,
|
|
57159
|
+
nodes,
|
|
57160
|
+
...edges.length > 0 ? { edges } : {},
|
|
57161
|
+
sources,
|
|
57162
|
+
summary: summary(sources)
|
|
57163
|
+
};
|
|
57164
|
+
}
|
|
56838
57165
|
|
|
56839
57166
|
// src/workflow/alignConstraintRegistry.ts
|
|
56840
57167
|
function duplicateValues2(values) {
|
|
@@ -56917,7 +57244,7 @@ function structureIssueHint(issue) {
|
|
|
56917
57244
|
const availableNodeSlugs = sortedUnique(context.availableNodeSlugs ?? []);
|
|
56918
57245
|
const availableBlockIds = sortedUnique(context.availableBlockIds ?? []);
|
|
56919
57246
|
const gateHelp = path8.includes("action_gate") || path8.includes("domain_gate") ? " For gate fields, follow plugin/skills/skill-align-workflow/references/gates.md." : "";
|
|
56920
|
-
const nextAction = context.reasonCode === "unknown-node-ref" ? `Use one of agent_hints[].available_node_refs for *_ref fields, then resubmit the corrected align-structure-decision.${gateHelp}` : context.correctShape !== undefined ? `Reshape this entry to match agent_hints[].correct_shape and resubmit the structure decision.${gateHelp}` : `Read \`context schema align-structure-decision\` and resubmit the structure decision.${gateHelp}`;
|
|
57247
|
+
const nextAction = context.reasonCode === "unknown-node-ref" ? `Use one of agent_hints[].available_node_refs for *_ref fields, then resubmit the corrected align-structure-decision.${gateHelp}` : context.reasonCode === "existing-node-type-drift" ? "Do not change node_type for an existing or previously removed slug. In incremental align, reference the existing slug from contains_parent, edges, domain_gate.child_refs, or block_ownership instead of redeclaring it. If the concept really needs a different type, use a new slug; if the old slug should be retired first, use `context drop` or an explicit structure correction path. `context align --scan --full` does not bypass this guard." : context.reasonCode === "section-owner-not-current-node" ? "sections[] plans Sections only for nodes declared in the current align-structure-decision. Move this Section plan to a current node or omit it for the previous finalized Node; previous Nodes can still be referenced from contains_parent, edges, domain_gate.child_refs, and block_ownership visibility." : context.correctShape !== undefined ? `Reshape this entry to match agent_hints[].correct_shape and resubmit the structure decision.${gateHelp}` : `Read \`context schema align-structure-decision\` and resubmit the structure decision.${gateHelp}`;
|
|
56921
57248
|
return {
|
|
56922
57249
|
code: "align-structure-decision-invalid",
|
|
56923
57250
|
severity: "error",
|
|
@@ -57245,7 +57572,8 @@ var SCHEMAS = {
|
|
|
57245
57572
|
"density_profile must be one of macro, meso, micro, or single_pass.",
|
|
57246
57573
|
"content_signals keys must be temporal_density, actor_density, step_density, or directive_density.",
|
|
57247
57574
|
"content_signals values must be high, med, or low. Use med, not medium.",
|
|
57248
|
-
"content_signals describe text shape only; they are not action/type/tag hints."
|
|
57575
|
+
"content_signals describe text shape only; they are not action/type/tag hints.",
|
|
57576
|
+
"align-coarse-read is a per-source input and latest checkpoint. Durable multi-source recall lives in align-candidate-ledger.source_readings."
|
|
57249
57577
|
]
|
|
57250
57578
|
},
|
|
57251
57579
|
"align-candidate-ops": {
|
|
@@ -57346,6 +57674,15 @@ var SCHEMAS = {
|
|
|
57346
57674
|
schema_version: "align.candidate-ledger.v1",
|
|
57347
57675
|
last_batch_id: "discovery_batch_0001",
|
|
57348
57676
|
id_mappings_from_last_batch: [{ local_candidate_ref: "local:region", candidate_id: "cand_0001" }],
|
|
57677
|
+
source_readings: [{
|
|
57678
|
+
source_id: "local:demo",
|
|
57679
|
+
segments_digest: "sha256:segments",
|
|
57680
|
+
coarse_read_digest: "sha256:coarse",
|
|
57681
|
+
read_at: "2026-05-12T00:00:00.000Z",
|
|
57682
|
+
density_profile: "single_pass",
|
|
57683
|
+
reading_anchors: [{ anchor_id: "a0001", evidence_blocks: ["b0001"] }],
|
|
57684
|
+
section_proposals: [{ section_id: "s0001", block_ids: ["b0001"], context_prefix: "Overview." }]
|
|
57685
|
+
}],
|
|
57349
57686
|
candidates: [{
|
|
57350
57687
|
candidate_id: "cand_0001",
|
|
57351
57688
|
current_slug: "data-region",
|
|
@@ -57361,6 +57698,9 @@ var SCHEMAS = {
|
|
|
57361
57698
|
block_dispositions: []
|
|
57362
57699
|
},
|
|
57363
57700
|
notes: [
|
|
57701
|
+
"source_readings stores per-source coarse-read notes so multiple coarse-read submissions do not rely on the latest align-coarse-read checkpoint.",
|
|
57702
|
+
"source_readings is discovery memory only; final structure still comes from align-structure-decision and finalized ownership.",
|
|
57703
|
+
"source_readings are sorted by source_id for deterministic output; they are not an execution-order log.",
|
|
57364
57704
|
"Batch-local refs may appear only in id_mappings_from_last_batch and must not leak into later batches.",
|
|
57365
57705
|
"Merged and superseded chains must remain acyclic."
|
|
57366
57706
|
]
|
|
@@ -57492,7 +57832,14 @@ var SCHEMAS = {
|
|
|
57492
57832
|
evidence_blocks: ["b0002"],
|
|
57493
57833
|
rationale: "Failover depends on the region scope."
|
|
57494
57834
|
}],
|
|
57495
|
-
|
|
57835
|
+
block_ownership_defaults: [{
|
|
57836
|
+
source_id: "local:regional-ops",
|
|
57837
|
+
ownership_role: "owned",
|
|
57838
|
+
owners: ["data-region"],
|
|
57839
|
+
visible_to: ["data-region", "regional-operations"],
|
|
57840
|
+
reason: "Remaining blocks in this source describe the data-region term."
|
|
57841
|
+
}],
|
|
57842
|
+
block_ownership: BLOCK_OWNERSHIP_EXAMPLE.filter((item) => item.block_id !== "b0002"),
|
|
57496
57843
|
unresolved: [
|
|
57497
57844
|
{
|
|
57498
57845
|
question_id: "q_failover_owner",
|
|
@@ -57505,16 +57852,22 @@ var SCHEMAS = {
|
|
|
57505
57852
|
"Use align-segments.generation_policy when choosing node.title, node.summary, rationale prose, and planned Section wording.",
|
|
57506
57853
|
"nodes[].contains_parent is the only hierarchy field; edges[] only accepts depends_on.",
|
|
57507
57854
|
"domain nodes need domain_gate; unresolved or weak domain gates are downgraded to entity.",
|
|
57855
|
+
"When align-segments.incremental.mode is incremental, finalize preserves previous finalized ownership for unscanned sources. The current decision should describe only the scanned evidence while referencing previous active Nodes when needed.",
|
|
57856
|
+
"In incremental mode, absence of an old Node or edge is not a deletion signal; use a full align, drop source, or explicit structure correction path for removals.",
|
|
57857
|
+
"Incremental finalize may reference previous active Nodes from nodes[].contains_parent, edges[].from/to, domain_gate.child_refs, and block_ownership owners/visible_to/primary_owner. Do not redeclare an old parent/domain just to attach new children.",
|
|
57858
|
+
"sections[].owner is intentionally limited to nodes declared in the current align-structure-decision payload; do not plan new Sections for previous finalized Nodes in an incremental payload.",
|
|
57859
|
+
"Existing and previously removed Node slugs cannot change node_type in either incremental or full finalize. context align --scan --full controls replacement/removal semantics, not type-drift bypass. Use a new slug for a different type, or retire the old slug through context drop or explicit structure correction first.",
|
|
57508
57860
|
"action_probe is the only place for action qualification booleans; action_gate accepts inference_sources only.",
|
|
57509
57861
|
"If the current candidate ledger already has action_probe for this action node and the digest matches, finalize may hydrate it. Directly new action nodes must provide action_probe explicitly.",
|
|
57510
57862
|
"action_gate.inference_sources needs actor, outcome_or_goal, repeatability_or_plan, and answerability. source_type must be one of explicit-block / heading-and-block / ref-node / inferred-from-block. explicit-block and heading-and-block entries may omit rationale; ref-node and inferred-from-block mappings must explain rationale.",
|
|
57863
|
+
"For large finalize payloads, use block_ownership_defaults[] to assign all remaining coverable blocks from a source_id to one role/owner, then use block_ownership[] only for block-level exceptions. The CLI expands defaults before validation; explicit block_ownership[] entries override defaults for the same block_id.",
|
|
57511
57864
|
"Each block_ownership[] entry sets `ownership_role` to one of owned / shared / context_only / ignored / unresolved.",
|
|
57512
57865
|
"owned: exactly one slug in `owners[]`, plus `visible_to[]`. Do not set `primary_owner` or `context_prefix`. reason is optional for mechanical owned cases.",
|
|
57513
57866
|
"shared: at least two slugs in `owners[]`, plus `primary_owner` chosen from those owners, plus `visible_to[]` and `reason`. The primary owner is the only Node allowed to author cited Sections from that block; secondary owners must request full text or raise an ownership challenge before citing it.",
|
|
57514
57867
|
"context_only: omit `owners` and `primary_owner` entirely. Required: `context_prefix` (short summary travelling with citing Sections), `visible_to[]`. reason is optional for mechanical context-only cases. The block carries context but is not citation evidence on its own.",
|
|
57515
57868
|
"ignored: omit `owners`, `primary_owner`, `context_prefix`, and `visible_to`. reason is optional for mechanical ignored cases. Use for outdated markers, navigation/external-link blocks, and placeholders without independent knowledge.",
|
|
57516
57869
|
"unresolved: omit `owners`, `primary_owner`, and `context_prefix`. Required: `question_id` (matching a top-level `unresolved[].question_id`) and `reason`. Use when classification is blocked by missing evidence.",
|
|
57517
|
-
"Every coverable block must have exactly one ownership disposition; the validator returns `agent_hints[].correct_shape` with the exact JSON skeleton when a role-specific field is wrong.",
|
|
57870
|
+
"Every coverable block must have exactly one ownership disposition after block_ownership_defaults[] expansion; the validator returns `agent_hints[].correct_shape` with the exact JSON skeleton when a role-specific field is wrong.",
|
|
57518
57871
|
"block_ownership assigns evidence visibility and citation authority only; write Section kinds in sections[].section_kind."
|
|
57519
57872
|
]
|
|
57520
57873
|
}
|
|
@@ -57527,6 +57880,92 @@ function alignWorkflowSchemaExample(name) {
|
|
|
57527
57880
|
}
|
|
57528
57881
|
|
|
57529
57882
|
// src/workflow/alignStructureOwnership.ts
|
|
57883
|
+
function expandOwnershipDefaults(input) {
|
|
57884
|
+
if (input.blockOwnershipDefaults === undefined)
|
|
57885
|
+
return input.blockOwnership;
|
|
57886
|
+
if (!Array.isArray(input.blockOwnership))
|
|
57887
|
+
return input.blockOwnership;
|
|
57888
|
+
if (!Array.isArray(input.blockOwnershipDefaults)) {
|
|
57889
|
+
throwStructureIssues([{
|
|
57890
|
+
path: "block_ownership_defaults",
|
|
57891
|
+
message: "block_ownership_defaults must be an array when present",
|
|
57892
|
+
context: {}
|
|
57893
|
+
}]);
|
|
57894
|
+
}
|
|
57895
|
+
if (input.coverableBlocks === undefined) {
|
|
57896
|
+
throwStructureIssues([{
|
|
57897
|
+
path: "block_ownership_defaults",
|
|
57898
|
+
message: "block_ownership_defaults requires align-segments source metadata",
|
|
57899
|
+
context: {}
|
|
57900
|
+
}]);
|
|
57901
|
+
}
|
|
57902
|
+
const sourceByBlock = new Map(input.coverableBlocks.map((block) => [block.block_id, block.source_id]));
|
|
57903
|
+
const blocksBySource = new Map;
|
|
57904
|
+
for (const blockId2 of input.coverableBlockIds) {
|
|
57905
|
+
const sourceId = sourceByBlock.get(blockId2);
|
|
57906
|
+
if (sourceId === undefined)
|
|
57907
|
+
continue;
|
|
57908
|
+
const blocks = blocksBySource.get(sourceId) ?? [];
|
|
57909
|
+
blocks.push(blockId2);
|
|
57910
|
+
blocksBySource.set(sourceId, blocks);
|
|
57911
|
+
}
|
|
57912
|
+
const explicitBlockIds = new Set(input.blockOwnership.filter(isRecord28).map((entry) => stringValue2(entry.block_id)).filter((blockId2) => blockId2 !== undefined));
|
|
57913
|
+
const issues = [];
|
|
57914
|
+
const defaultsBySource = new Set;
|
|
57915
|
+
const expanded = [];
|
|
57916
|
+
input.blockOwnershipDefaults.forEach((raw, index) => {
|
|
57917
|
+
if (!isRecord28(raw)) {
|
|
57918
|
+
issues.push({ path: `block_ownership_defaults[${index}]`, message: "ownership default must be an object", context: {} });
|
|
57919
|
+
return;
|
|
57920
|
+
}
|
|
57921
|
+
if (raw.block_id !== undefined) {
|
|
57922
|
+
issues.push({
|
|
57923
|
+
path: `block_ownership_defaults[${index}].block_id`,
|
|
57924
|
+
message: "block_ownership_defaults entries must omit block_id; the CLI expands them across source blocks",
|
|
57925
|
+
context: {}
|
|
57926
|
+
});
|
|
57927
|
+
return;
|
|
57928
|
+
}
|
|
57929
|
+
const sourceId = stringValue2(raw.source_id);
|
|
57930
|
+
if (sourceId === undefined) {
|
|
57931
|
+
issues.push({
|
|
57932
|
+
path: `block_ownership_defaults[${index}].source_id`,
|
|
57933
|
+
message: "source_id is required",
|
|
57934
|
+
context: { availableBlockIds: input.coverableBlockIds }
|
|
57935
|
+
});
|
|
57936
|
+
return;
|
|
57937
|
+
}
|
|
57938
|
+
if (defaultsBySource.has(sourceId)) {
|
|
57939
|
+
issues.push({
|
|
57940
|
+
path: `block_ownership_defaults[${index}].source_id`,
|
|
57941
|
+
message: `duplicate ownership default for source "${sourceId}"`,
|
|
57942
|
+
context: { reasonCode: "duplicate-source-ownership-default" }
|
|
57943
|
+
});
|
|
57944
|
+
return;
|
|
57945
|
+
}
|
|
57946
|
+
defaultsBySource.add(sourceId);
|
|
57947
|
+
const blockIds = blocksBySource.get(sourceId);
|
|
57948
|
+
if (blockIds === undefined || blockIds.length === 0) {
|
|
57949
|
+
issues.push({
|
|
57950
|
+
path: `block_ownership_defaults[${index}].source_id`,
|
|
57951
|
+
message: `source "${sourceId}" has no coverable blocks in this align-segments payload`,
|
|
57952
|
+
context: {
|
|
57953
|
+
reasonCode: "unknown-source-ownership-default",
|
|
57954
|
+
availableBlockIds: input.coverableBlockIds
|
|
57955
|
+
}
|
|
57956
|
+
});
|
|
57957
|
+
return;
|
|
57958
|
+
}
|
|
57959
|
+
for (const blockId2 of blockIds) {
|
|
57960
|
+
if (explicitBlockIds.has(blockId2))
|
|
57961
|
+
continue;
|
|
57962
|
+
expanded.push({ ...raw, block_id: blockId2 });
|
|
57963
|
+
}
|
|
57964
|
+
});
|
|
57965
|
+
if (issues.length > 0)
|
|
57966
|
+
throwStructureIssues(issues);
|
|
57967
|
+
return [...expanded, ...input.blockOwnership];
|
|
57968
|
+
}
|
|
57530
57969
|
function normalizeOwnership(value, refs, slugs, coverableBlockIds) {
|
|
57531
57970
|
if (!Array.isArray(value))
|
|
57532
57971
|
throwStructureIssues([{
|
|
@@ -57770,22 +58209,47 @@ function setRef(map, ref, slug, path8) {
|
|
|
57770
58209
|
}
|
|
57771
58210
|
map.set(ref, slug);
|
|
57772
58211
|
}
|
|
57773
|
-
function refMap(nodes) {
|
|
58212
|
+
function refMap(nodes, previous) {
|
|
57774
58213
|
const map = new Map;
|
|
57775
58214
|
const slugs = new Set;
|
|
58215
|
+
const currentSlugs = new Set;
|
|
58216
|
+
for (const node2 of previous?.nodes ?? []) {
|
|
58217
|
+
slugs.add(node2.slug);
|
|
58218
|
+
setRef(map, node2.slug, node2.slug, "previous.nodes[].slug");
|
|
58219
|
+
}
|
|
57776
58220
|
for (const node2 of nodes) {
|
|
57777
|
-
if (
|
|
58221
|
+
if (currentSlugs.has(node2.slug)) {
|
|
57778
58222
|
reject3("nodes[].slug", `duplicate node slug "${node2.slug}"`, {
|
|
57779
58223
|
reasonCode: "duplicate-node-slug",
|
|
57780
|
-
availableNodeSlugs: [...slugs]
|
|
58224
|
+
availableNodeSlugs: [...slugs, node2.slug]
|
|
57781
58225
|
});
|
|
57782
58226
|
}
|
|
58227
|
+
currentSlugs.add(node2.slug);
|
|
58228
|
+
if (slugs.has(node2.slug)) {
|
|
58229
|
+
const existingType = previous?.nodes?.find((candidate) => candidate.slug === node2.slug)?.type;
|
|
58230
|
+
if (existingType === undefined) {
|
|
58231
|
+
reject3("nodes[].slug", `duplicate node slug "${node2.slug}"`, {
|
|
58232
|
+
reasonCode: "duplicate-node-slug",
|
|
58233
|
+
availableNodeSlugs: [...slugs]
|
|
58234
|
+
});
|
|
58235
|
+
}
|
|
58236
|
+
}
|
|
57783
58237
|
slugs.add(node2.slug);
|
|
57784
58238
|
setRef(map, node2.slug, node2.slug, "nodes[].slug");
|
|
57785
58239
|
setRef(map, node2.llm_slug_hint, node2.slug, "nodes[].llm_slug_hint");
|
|
57786
58240
|
}
|
|
57787
58241
|
return map;
|
|
57788
58242
|
}
|
|
58243
|
+
function previousNodeTypes(previous) {
|
|
58244
|
+
const types4 = new Map;
|
|
58245
|
+
for (const node2 of previous?.removed_nodes ?? []) {
|
|
58246
|
+
types4.set(node2.slug, node2.type);
|
|
58247
|
+
}
|
|
58248
|
+
for (const node2 of previous?.nodes ?? []) {
|
|
58249
|
+
types4.set(node2.slug, node2.type);
|
|
58250
|
+
}
|
|
58251
|
+
return types4;
|
|
58252
|
+
}
|
|
57789
58253
|
function resolveRef2(refs, value, path8) {
|
|
57790
58254
|
if (value === null || value === undefined)
|
|
57791
58255
|
return value;
|
|
@@ -57800,6 +58264,9 @@ function resolveRef2(refs, value, path8) {
|
|
|
57800
58264
|
}
|
|
57801
58265
|
return resolved;
|
|
57802
58266
|
}
|
|
58267
|
+
function nodeRefsForSlugs(refs, slugs) {
|
|
58268
|
+
return [...refs.entries()].filter(([, slug]) => slugs.has(slug)).map(([ref]) => ref).sort();
|
|
58269
|
+
}
|
|
57803
58270
|
function parseNodes(value) {
|
|
57804
58271
|
if (!Array.isArray(value))
|
|
57805
58272
|
reject3("nodes", "nodes must be an array");
|
|
@@ -57906,8 +58373,7 @@ function downgradeNode(node2, warningKind, reason) {
|
|
|
57906
58373
|
}
|
|
57907
58374
|
};
|
|
57908
58375
|
}
|
|
57909
|
-
function normalizeNodes(nodes, refs) {
|
|
57910
|
-
const slugs = new Set(nodes.map((node2) => node2.slug));
|
|
58376
|
+
function normalizeNodes(nodes, refs, allSlugs, previousTypes) {
|
|
57911
58377
|
const warnings = [];
|
|
57912
58378
|
const normalized = nodes.map((node2, index) => {
|
|
57913
58379
|
const parent = resolveRef2(refs, node2.contains_parent_ref ?? node2.contains_parent, `nodes[${index}].contains_parent_ref`);
|
|
@@ -57915,21 +58381,40 @@ function normalizeNodes(nodes, refs) {
|
|
|
57915
58381
|
...node2,
|
|
57916
58382
|
...parent !== undefined ? { contains_parent: parent } : {}
|
|
57917
58383
|
};
|
|
57918
|
-
if (next.contains_parent !== null && next.contains_parent !== undefined && !
|
|
58384
|
+
if (next.contains_parent !== null && next.contains_parent !== undefined && !allSlugs.has(next.contains_parent)) {
|
|
57919
58385
|
reject3(`nodes[${index}].contains_parent`, "contains_parent must reference a finalized node", {
|
|
57920
58386
|
reasonCode: "unknown-finalized-parent",
|
|
57921
58387
|
availableNodeRefs: nodeRefsForHint(refs),
|
|
57922
|
-
availableNodeSlugs: [...
|
|
58388
|
+
availableNodeSlugs: [...allSlugs]
|
|
57923
58389
|
});
|
|
57924
58390
|
}
|
|
57925
58391
|
if (next.node_type === "domain") {
|
|
57926
58392
|
const gate = next.domain_gate;
|
|
57927
|
-
const
|
|
57928
|
-
const
|
|
58393
|
+
const rawChildRefs = Array.isArray(gate?.child_refs) ? gate.child_refs : undefined;
|
|
58394
|
+
const childRefs = rawChildRefs?.map((ref) => typeof ref === "string" ? ref.trim() : "") ?? [];
|
|
58395
|
+
const validGate = gate !== undefined && Array.isArray(gate.scope_blocks) && rawChildRefs !== undefined && typeof gate.grouping_reason === "string" && childRefs.length === rawChildRefs.length && childRefs.every((ref) => ref.length > 0 && (refs.has(ref) || allSlugs.has(ref)));
|
|
57929
58396
|
if (!validGate) {
|
|
57930
58397
|
const downgraded = downgradeNode(next, "domain_downgrade", "domain_gate is missing or references unresolved child refs");
|
|
57931
58398
|
next = downgraded.node;
|
|
57932
58399
|
warnings.push(downgraded.warning);
|
|
58400
|
+
} else {
|
|
58401
|
+
const canonicalChildRefs = childRefs.map((ref) => refs.get(ref) ?? ref);
|
|
58402
|
+
const duplicateChildRef = canonicalChildRefs.find((ref, refIndex) => canonicalChildRefs.indexOf(ref) !== refIndex);
|
|
58403
|
+
if (duplicateChildRef !== undefined) {
|
|
58404
|
+
reject3(`nodes[${index}].domain_gate.child_refs`, `domain_gate.child_refs contains duplicate child "${duplicateChildRef}"`, {
|
|
58405
|
+
reasonCode: "duplicate-domain-child-ref",
|
|
58406
|
+
failedRef: duplicateChildRef,
|
|
58407
|
+
availableNodeRefs: nodeRefsForHint(refs),
|
|
58408
|
+
availableNodeSlugs: [...allSlugs]
|
|
58409
|
+
});
|
|
58410
|
+
}
|
|
58411
|
+
next = {
|
|
58412
|
+
...next,
|
|
58413
|
+
domain_gate: {
|
|
58414
|
+
...gate,
|
|
58415
|
+
child_refs: canonicalChildRefs
|
|
58416
|
+
}
|
|
58417
|
+
};
|
|
57933
58418
|
}
|
|
57934
58419
|
}
|
|
57935
58420
|
if (next.node_type === "action") {
|
|
@@ -57945,6 +58430,14 @@ function normalizeNodes(nodes, refs) {
|
|
|
57945
58430
|
validateInferenceSources(gate, `nodes[${index}].action_gate`);
|
|
57946
58431
|
}
|
|
57947
58432
|
}
|
|
58433
|
+
const previousType = previousTypes.get(next.slug);
|
|
58434
|
+
if (previousType !== undefined && previousType !== next.node_type) {
|
|
58435
|
+
reject3(`nodes[${index}].node_type`, `existing node "${next.slug}" is already ${previousType}; align finalize cannot change it to ${next.node_type}`, {
|
|
58436
|
+
reasonCode: "existing-node-type-drift",
|
|
58437
|
+
availableNodeRefs: nodeRefsForHint(refs),
|
|
58438
|
+
availableNodeSlugs: [...allSlugs]
|
|
58439
|
+
});
|
|
58440
|
+
}
|
|
57948
58441
|
const plannedSections = next.planned_sections ?? [];
|
|
57949
58442
|
for (const issue of alignStructureNodeConstraintIssues({ node: next, nodeIndex: index })) {
|
|
57950
58443
|
reject3(issue.path, issue.message, { reasonCode: issue.code });
|
|
@@ -57973,11 +58466,18 @@ function hydrateActionProbesFromLedger(nodes, input) {
|
|
|
57973
58466
|
return actionProbe !== undefined ? { ...node2, action_probe: actionProbe } : node2;
|
|
57974
58467
|
});
|
|
57975
58468
|
}
|
|
57976
|
-
function assertNoContainsCycles(nodes, refs) {
|
|
58469
|
+
function assertNoContainsCycles(nodes, refs, previous) {
|
|
57977
58470
|
const parentBySlug = new Map;
|
|
58471
|
+
for (const node2 of previous?.nodes ?? []) {
|
|
58472
|
+
if (typeof node2.contains_parent === "string" && node2.contains_parent.length > 0) {
|
|
58473
|
+
parentBySlug.set(node2.slug, node2.contains_parent);
|
|
58474
|
+
}
|
|
58475
|
+
}
|
|
57978
58476
|
nodes.forEach((node2) => {
|
|
57979
58477
|
if (typeof node2.contains_parent === "string" && node2.contains_parent.length > 0) {
|
|
57980
58478
|
parentBySlug.set(node2.slug, node2.contains_parent);
|
|
58479
|
+
} else {
|
|
58480
|
+
parentBySlug.delete(node2.slug);
|
|
57981
58481
|
}
|
|
57982
58482
|
});
|
|
57983
58483
|
nodes.forEach((node2, index) => {
|
|
@@ -57989,7 +58489,7 @@ function assertNoContainsCycles(nodes, refs) {
|
|
|
57989
58489
|
reasonCode: "contains-parent-cycle",
|
|
57990
58490
|
failedRef: current,
|
|
57991
58491
|
availableNodeRefs: nodeRefsForHint(refs),
|
|
57992
|
-
availableNodeSlugs:
|
|
58492
|
+
availableNodeSlugs: nodeSlugsForHint(refs)
|
|
57993
58493
|
});
|
|
57994
58494
|
}
|
|
57995
58495
|
seen.add(current);
|
|
@@ -57997,18 +58497,30 @@ function assertNoContainsCycles(nodes, refs) {
|
|
|
57997
58497
|
}
|
|
57998
58498
|
});
|
|
57999
58499
|
}
|
|
58000
|
-
function normalizeSections(value, refs,
|
|
58500
|
+
function normalizeSections(value, refs, currentSlugs) {
|
|
58001
58501
|
if (!Array.isArray(value))
|
|
58002
58502
|
reject3("sections", "sections must be an array");
|
|
58503
|
+
const currentRefs = nodeRefsForSlugs(refs, currentSlugs);
|
|
58003
58504
|
return value.map((raw, index) => {
|
|
58004
58505
|
if (!isRecord28(raw))
|
|
58005
58506
|
reject3(`sections[${index}]`, "section must be an object");
|
|
58006
|
-
const
|
|
58007
|
-
if (
|
|
58008
|
-
reject3(`sections[${index}].owner`,
|
|
58009
|
-
|
|
58010
|
-
|
|
58011
|
-
|
|
58507
|
+
const rawOwner = stringValue2(raw.owner);
|
|
58508
|
+
if (rawOwner === undefined)
|
|
58509
|
+
reject3(`sections[${index}].owner`, `sections[${index}].owner is required`);
|
|
58510
|
+
const owner = refs.get(rawOwner);
|
|
58511
|
+
if (owner === undefined) {
|
|
58512
|
+
reject3(`sections[${index}].owner`, `sections[${index}].owner references unknown node "${rawOwner}"`, {
|
|
58513
|
+
reasonCode: "unknown-node-ref",
|
|
58514
|
+
failedRef: rawOwner,
|
|
58515
|
+
availableNodeRefs: currentRefs,
|
|
58516
|
+
availableNodeSlugs: [...currentSlugs]
|
|
58517
|
+
});
|
|
58518
|
+
}
|
|
58519
|
+
if (!owner || !currentSlugs.has(owner)) {
|
|
58520
|
+
reject3(`sections[${index}].owner`, "owner must reference a node declared in this align-structure-decision payload", {
|
|
58521
|
+
reasonCode: "section-owner-not-current-node",
|
|
58522
|
+
availableNodeRefs: currentRefs,
|
|
58523
|
+
availableNodeSlugs: [...currentSlugs]
|
|
58012
58524
|
});
|
|
58013
58525
|
}
|
|
58014
58526
|
const rationale = stringValue2(raw.rationale);
|
|
@@ -58074,12 +58586,18 @@ function normalizeAlignStructureDecision(input) {
|
|
|
58074
58586
|
});
|
|
58075
58587
|
}
|
|
58076
58588
|
const parsedNodes = hydrateActionProbesFromLedger(parseNodes(input.payload.nodes), input);
|
|
58077
|
-
const
|
|
58078
|
-
const
|
|
58079
|
-
const
|
|
58080
|
-
|
|
58081
|
-
const
|
|
58082
|
-
|
|
58589
|
+
const previousForRefs = input.allowExistingNodeRefs === true ? input.previousOwnership : undefined;
|
|
58590
|
+
const refs = refMap(parsedNodes, previousForRefs);
|
|
58591
|
+
const previousTypes = previousNodeTypes(input.previousOwnership);
|
|
58592
|
+
const currentSlugs = new Set(parsedNodes.map((node2) => node2.slug));
|
|
58593
|
+
const allSlugs = new Set([
|
|
58594
|
+
...currentSlugs,
|
|
58595
|
+
...input.allowExistingNodeRefs === true ? (input.previousOwnership?.nodes ?? []).map((node2) => node2.slug) : []
|
|
58596
|
+
]);
|
|
58597
|
+
const normalizedNodes = normalizeNodes(parsedNodes, refs, allSlugs, previousTypes);
|
|
58598
|
+
assertNoContainsCycles(normalizedNodes.nodes, refs, input.previousOwnership);
|
|
58599
|
+
const sections = normalizeSections(input.payload.sections, refs, currentSlugs);
|
|
58600
|
+
const edges = normalizeEdges(input.payload.edges, refs, allSlugs);
|
|
58083
58601
|
validateStructureEvidence({
|
|
58084
58602
|
nodes: normalizedNodes.nodes,
|
|
58085
58603
|
sections,
|
|
@@ -58087,7 +58605,13 @@ function normalizeAlignStructureDecision(input) {
|
|
|
58087
58605
|
coverableBlockIds: input.coverableBlockIds,
|
|
58088
58606
|
reject: reject3
|
|
58089
58607
|
});
|
|
58090
|
-
const
|
|
58608
|
+
const ownershipInput = expandOwnershipDefaults({
|
|
58609
|
+
blockOwnership: input.payload.block_ownership,
|
|
58610
|
+
blockOwnershipDefaults: input.payload.block_ownership_defaults,
|
|
58611
|
+
coverableBlockIds: input.coverableBlockIds,
|
|
58612
|
+
...input.coverableBlocks !== undefined ? { coverableBlocks: input.coverableBlocks } : {}
|
|
58613
|
+
});
|
|
58614
|
+
const blockOwnership = normalizeOwnership(ownershipInput, refs, allSlugs, input.coverableBlockIds);
|
|
58091
58615
|
const unresolved = normalizeUnresolved(input.payload.unresolved);
|
|
58092
58616
|
validateUnresolvedQuestions({ blockOwnership, unresolved, reject: reject3 });
|
|
58093
58617
|
const artifactKind = stringValue2(input.payload.artifact_kind);
|
|
@@ -58225,18 +58749,27 @@ async function finalizeAlignStructureDecisionCommand(input) {
|
|
|
58225
58749
|
digest: input.workflowState.input_digests["align-candidate-ledger"],
|
|
58226
58750
|
workflowInputDigests: input.workflowState.input_digests
|
|
58227
58751
|
})).value : undefined;
|
|
58752
|
+
const previousOwnership = await readCurrentSourceOwnership(input.ctxDir);
|
|
58753
|
+
const isIncrementalFinalize = segments.incremental.mode === "incremental";
|
|
58228
58754
|
const rawDecision = await readStructuredInput2(finalizePath);
|
|
58755
|
+
const coverableBlockSet = new Set(segments.coverage.coverable_block_ids);
|
|
58229
58756
|
const decision = normalizeAlignStructureDecision({
|
|
58230
58757
|
payload: rawDecision,
|
|
58231
58758
|
coverableBlockIds: segments.coverage.coverable_block_ids,
|
|
58232
|
-
|
|
58759
|
+
coverableBlocks: segments.sources.flatMap((source2) => source2.blocks.filter((block) => coverableBlockSet.has(block.block_id)).map((block) => ({ block_id: block.block_id, source_id: source2.source_id }))),
|
|
58760
|
+
...candidateLedger !== undefined ? { candidateLedger } : {},
|
|
58761
|
+
...previousOwnership !== null ? { previousOwnership } : {},
|
|
58762
|
+
allowExistingNodeRefs: isIncrementalFinalize
|
|
58233
58763
|
});
|
|
58234
|
-
const
|
|
58235
|
-
const ownership = buildSourceOwnershipFromFinalized({
|
|
58764
|
+
const currentOwnership = buildSourceOwnershipFromFinalized({
|
|
58236
58765
|
segments,
|
|
58237
58766
|
decision,
|
|
58238
58767
|
now: new Date
|
|
58239
58768
|
});
|
|
58769
|
+
const ownership = isIncrementalFinalize && previousOwnership !== null ? mergeIncrementalSourceOwnership({
|
|
58770
|
+
previous: previousOwnership,
|
|
58771
|
+
current: currentOwnership
|
|
58772
|
+
}) : currentOwnership;
|
|
58240
58773
|
const citationIssues = alignFinalizeConstraintIssues({ ownership });
|
|
58241
58774
|
if (citationIssues.length > 0) {
|
|
58242
58775
|
throw new ContextError(ExitCode.UserError, `align finalize rejected ${citationIssues.length} Node(s) without citation-eligible owned/shared-primary evidence`, {
|
|
@@ -58277,7 +58810,8 @@ async function finalizeAlignStructureDecisionCommand(input) {
|
|
|
58277
58810
|
const removedNodes = finalizedRemovedNodes({
|
|
58278
58811
|
previous: previousOwnership,
|
|
58279
58812
|
next: ownership,
|
|
58280
|
-
workflowState
|
|
58813
|
+
workflowState,
|
|
58814
|
+
mode: segments.incremental.mode
|
|
58281
58815
|
});
|
|
58282
58816
|
const publishedOwnership = {
|
|
58283
58817
|
...ownership,
|
|
@@ -58364,6 +58898,8 @@ async function finalizeAlignStructureDecisionCommand(input) {
|
|
|
58364
58898
|
function finalizedRemovedNodes(input) {
|
|
58365
58899
|
const nextSlugs = new Set((input.next.nodes ?? []).map((node2) => node2.slug));
|
|
58366
58900
|
const carried = (input.previous?.removed_nodes ?? []).filter((node2) => !nextSlugs.has(node2.slug));
|
|
58901
|
+
if (input.mode === "incremental")
|
|
58902
|
+
return carried.sort((left, right) => left.slug.localeCompare(right.slug));
|
|
58367
58903
|
const carriedSlugs = new Set(carried.map((node2) => node2.slug));
|
|
58368
58904
|
const newlyRemoved = (input.previous?.nodes ?? []).filter((node2) => !nextSlugs.has(node2.slug) && !carriedSlugs.has(node2.slug)).map((node2) => ({
|
|
58369
58905
|
slug: node2.slug,
|
|
@@ -58910,6 +59446,25 @@ async function runAlignCoarseReadCommand(input) {
|
|
|
58910
59446
|
value: coarseRead,
|
|
58911
59447
|
format: "yaml"
|
|
58912
59448
|
});
|
|
59449
|
+
const previousLedger = await readExistingLedger({
|
|
59450
|
+
ctxDir: input.ctxDir,
|
|
59451
|
+
workflowState: input.workflowState
|
|
59452
|
+
});
|
|
59453
|
+
const ledger = upsertSourceReading({
|
|
59454
|
+
...previousLedger !== undefined ? { ledger: previousLedger } : {},
|
|
59455
|
+
sourceId: coarseRead.source_id,
|
|
59456
|
+
segmentsDigest,
|
|
59457
|
+
coarseReadDigest: payloadRecord.digest,
|
|
59458
|
+
coarseRead
|
|
59459
|
+
});
|
|
59460
|
+
const ledgerRecord = await writeWorkflowPayloadWithDigest({
|
|
59461
|
+
ctxDir: input.ctxDir,
|
|
59462
|
+
workflowId: input.workflowState.workflow_id,
|
|
59463
|
+
scopeId: workflowScope(input.workflowState),
|
|
59464
|
+
payload: "align-candidate-ledger",
|
|
59465
|
+
value: ledger,
|
|
59466
|
+
format: "json"
|
|
59467
|
+
});
|
|
58913
59468
|
const workflowState = await advanceCurrentWorkflow({
|
|
58914
59469
|
ctxDir: input.ctxDir,
|
|
58915
59470
|
family: "align",
|
|
@@ -58917,17 +59472,37 @@ async function runAlignCoarseReadCommand(input) {
|
|
|
58917
59472
|
scopeId: workflowScope(input.workflowState),
|
|
58918
59473
|
inputDigests: {
|
|
58919
59474
|
...input.workflowState.input_digests,
|
|
58920
|
-
"align-coarse-read": payloadRecord.digest
|
|
59475
|
+
"align-coarse-read": payloadRecord.digest,
|
|
59476
|
+
"align-candidate-ledger": ledgerRecord.digest
|
|
58921
59477
|
}
|
|
58922
59478
|
});
|
|
58923
59479
|
const payloadReceipt = workflowPayloadReceipt(payloadRecord, {
|
|
58924
59480
|
nextCommand: "context align --ops -"
|
|
58925
59481
|
});
|
|
59482
|
+
const ledgerReceipt = workflowPayloadReceipt(ledgerRecord, {
|
|
59483
|
+
view: ["--view ledger"],
|
|
59484
|
+
nextCommand: "context align --ops -"
|
|
59485
|
+
});
|
|
59486
|
+
const sourceId = coarseRead.source_id;
|
|
59487
|
+
const sourceReadingShowCommand = workflowPayloadShowCommand(ledgerRecord, [
|
|
59488
|
+
`--view ledger --source ${shellQuote(sourceId)}`,
|
|
59489
|
+
"--unwrap",
|
|
59490
|
+
"--format json"
|
|
59491
|
+
]);
|
|
58926
59492
|
if (input.format === "json") {
|
|
58927
59493
|
writeJson3({
|
|
58928
59494
|
action: "coarse-read-saved",
|
|
58929
59495
|
workflow: workflowMetadata(workflowState),
|
|
58930
59496
|
payload: payloadReceipt,
|
|
59497
|
+
ledger: ledgerReceipt,
|
|
59498
|
+
source_reading: {
|
|
59499
|
+
source_id: sourceId,
|
|
59500
|
+
show_command: sourceReadingShowCommand
|
|
59501
|
+
},
|
|
59502
|
+
summary: {
|
|
59503
|
+
source_readings: (ledger.source_readings ?? []).length,
|
|
59504
|
+
source_id: sourceId
|
|
59505
|
+
},
|
|
58931
59506
|
next: "produce candidate ops with context align --ops -"
|
|
58932
59507
|
});
|
|
58933
59508
|
return;
|
|
@@ -58940,7 +59515,9 @@ async function runAlignCoarseReadCommand(input) {
|
|
|
58940
59515
|
body: [
|
|
58941
59516
|
workflowSummaryLine(workflowState),
|
|
58942
59517
|
`digest: ${payloadRecord.digest}`,
|
|
58943
|
-
`show: ${workflowPayloadShowCommand(payloadRecord)}
|
|
59518
|
+
`show: ${workflowPayloadShowCommand(payloadRecord)}`,
|
|
59519
|
+
`candidate ledger: ${ledgerReceipt.show_command}`,
|
|
59520
|
+
`source reading: ${sourceReadingShowCommand}`
|
|
58944
59521
|
],
|
|
58945
59522
|
next: "produce candidate ops with context align --ops -"
|
|
58946
59523
|
}));
|
|
@@ -59341,9 +59918,11 @@ function collectRequestedBlock(value, previous) {
|
|
|
59341
59918
|
function ignoredSourceIds(value) {
|
|
59342
59919
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
|
|
59343
59920
|
}
|
|
59344
|
-
function coverageSkipMissingOptionsError(missingFields) {
|
|
59921
|
+
function coverageSkipMissingOptionsError(missingFields, mode = "single") {
|
|
59922
|
+
const flag = mode === "bulk" ? "--coverage-skip-unresolved" : "--coverage-skip";
|
|
59923
|
+
const command = mode === "bulk" ? 'context compile --coverage-skip-unresolved --coverage-disposition-node <slug> --payload-digest <digest> --reason "<reason>"' : 'context compile --coverage-skip <candidate-id> --coverage-disposition-node <slug> --payload-digest <digest> --reason "<reason>"';
|
|
59345
59924
|
const reasonOnly = missingFields.length === 1 && missingFields[0] === "--reason <text>";
|
|
59346
|
-
const message = reasonOnly ?
|
|
59925
|
+
const message = reasonOnly ? `${flag} requires --reason <text>` : `${flag} is missing required option(s): ${missingFields.join(", ")}`;
|
|
59347
59926
|
return new ContextError(ExitCode.UserError, message, {
|
|
59348
59927
|
category: ErrorCategory.UserInputInvalid,
|
|
59349
59928
|
missing_fields: missingFields,
|
|
@@ -59351,8 +59930,8 @@ function coverageSkipMissingOptionsError(missingFields) {
|
|
|
59351
59930
|
code: reasonOnly ? "coverage-skip-reason-required" : "coverage-skip-required-options",
|
|
59352
59931
|
severity: "error",
|
|
59353
59932
|
message: reasonOnly ? "Skipping coverage requires a reason for auditability." : "Coverage skip needs every non-inferable option in one retry.",
|
|
59354
|
-
next_action: "Retry with the candidate id, node, payload digest, and audit reason in one command.",
|
|
59355
|
-
command
|
|
59933
|
+
next_action: mode === "single" ? "Retry with the candidate id, node, payload digest, and audit reason in one command." : "Retry with the node, payload digest, and audit reason in one command.",
|
|
59934
|
+
command,
|
|
59356
59935
|
diagnostics: { missing_fields: missingFields }
|
|
59357
59936
|
}]
|
|
59358
59937
|
});
|
|
@@ -59362,7 +59941,7 @@ function compileStageForOptions(options) {
|
|
|
59362
59941
|
return "node_draft_ready";
|
|
59363
59942
|
if (typeof options.context === "string")
|
|
59364
59943
|
return "node_context_ready";
|
|
59365
|
-
if (typeof options.coverageDisposition === "string" || typeof options.coverageSkip === "string")
|
|
59944
|
+
if (typeof options.coverageDisposition === "string" || typeof options.coverageSkip === "string" || options.coverageSkipUnresolved === true)
|
|
59366
59945
|
return null;
|
|
59367
59946
|
if (typeof options.draft === "string")
|
|
59368
59947
|
return "node_draft_ready";
|
|
@@ -59407,7 +59986,7 @@ async function writeCompileDraftChallengePayloads2(input) {
|
|
|
59407
59986
|
function registerWorkflowCommands(program2) {
|
|
59408
59987
|
registerWorkflowStateCommands(program2);
|
|
59409
59988
|
registerAlignWorkflowCommand(program2);
|
|
59410
|
-
program2.command("compile").description("Workflow helpers for /context:compile").option("--scan-changes", "print incremental compile workset without creating a workflow lock").addOption(new Option("--scan", "removed; use --scan-changes").hideHelp()).option("--format <format>", "with --scan-changes/--context/--source-refs, output format: json | table", "table").option("--view <view>", "compact output view: full | summary | source-refs | issues").option("--ignore-source <source-id>", "with --scan-changes, --context, or --source-refs, ignore refresh-applied source ids", collectIgnoredSource, []).option("--context <slug>", "prepare NodeContext payload for one align node and print summary/digests").option("--source-refs <slug>", "print compact citation source_ref list for one align node").option("--request-full-text <block-id>", "with --context, expand one secondary shared finalized ownership block", collectRequestedBlock, []).option("--node-cycle <slug>", "validate one node draft, prepare reconcile, review safe defaults, and apply in one command").option("--draft <slug>", "validate a compile draft JSON/YAML document from stdin for one node").option("--prepare", "with --draft --plan, also prepare the semantic reconcile payload").option("--draft-status <slug>", "print the current compile draft session summary").option("--draft-patch <slug>", "apply a compile draft patch for one node").option("--input <stdin>", "compile draft JSON/YAML document used with --draft; use '-' for stdin").option("--accept-safe-defaults", "with --node-cycle, accept mechanically safe defaults and stop if anything needs judgment").option("--coverage-disposition <stdin>", "coverage disposition patch JSON/YAML document; use '-' for stdin").option("--coverage-skip <candidate-id>", "skip one coverage candidate without writing a disposition file").option("--coverage-disposition-node <slug>", "node slug for --coverage-disposition").option("--coverage-disposition-replace", "replace existing dispositions for the patched coverage candidates").option("--payload-digest <digest>", "expected workflow payload digest for write commands").option("--reason <text>", "reason for
|
|
59989
|
+
program2.command("compile").description("Workflow helpers for /context:compile").option("--scan-changes", "print incremental compile workset without creating a workflow lock").addOption(new Option("--scan", "removed; use --scan-changes").hideHelp()).option("--format <format>", "with --scan-changes/--context/--source-refs, output format: json | table", "table").option("--view <view>", "compact output view: full | summary | source-refs | issues").option("--ignore-source <source-id>", "with --scan-changes, --context, or --source-refs, ignore refresh-applied source ids", collectIgnoredSource, []).option("--context <slug>", "prepare NodeContext payload for one align node and print summary/digests").option("--source-refs <slug>", "print compact citation source_ref list for one align node").option("--request-full-text <block-id>", "with --context, expand one secondary shared finalized ownership block", collectRequestedBlock, []).option("--node-cycle <slug>", "validate one node draft, prepare reconcile, review safe defaults, and apply in one command").option("--draft <slug>", "validate a compile draft JSON/YAML document from stdin for one node").option("--prepare", "with --draft --plan, also prepare the semantic reconcile payload").option("--draft-status <slug>", "print the current compile draft session summary").option("--draft-patch <slug>", "apply a compile draft patch for one node").option("--input <stdin>", "compile draft JSON/YAML document used with --draft; use '-' for stdin").option("--accept-safe-defaults", "with --node-cycle, accept mechanically safe defaults and stop if anything needs judgment").option("--coverage-disposition <stdin>", "coverage disposition patch JSON/YAML document; use '-' for stdin").option("--coverage-skip <candidate-id>", "skip one coverage candidate without writing a disposition file").option("--coverage-skip-unresolved", "skip every unresolved coverage candidate in the node payload").option("--coverage-disposition-node <slug>", "node slug for --coverage-disposition").option("--coverage-disposition-replace", "replace existing dispositions for the patched coverage candidates").option("--payload-digest <digest>", "expected workflow payload digest for write commands").option("--reason <text>", "reason for coverage skip shortcuts").option("--plan", "validate the draft without writing knowledge").option("--close", "run compile close: compact, rebuild index, append changelog, verify").option("--save-input", "with --draft, save the consumed draft under output/compile/compile.<slug>.draft.yaml").action(async (options) => {
|
|
59411
59990
|
if (options.scan === true) {
|
|
59412
59991
|
throw new ContextError(ExitCode.UserError, "context compile --scan was removed; use context compile --scan-changes", {
|
|
59413
59992
|
category: ErrorCategory.UserInputInvalid,
|
|
@@ -59421,9 +60000,9 @@ function registerWorkflowCommands(program2) {
|
|
|
59421
60000
|
});
|
|
59422
60001
|
}
|
|
59423
60002
|
const wantsChanges = options.scanChanges === true;
|
|
59424
|
-
const actionCount = countActions(wantsChanges, typeof options.context === "string", typeof options.sourceRefs === "string", typeof options.nodeCycle === "string", typeof options.draft === "string", typeof options.draftStatus === "string", typeof options.draftPatch === "string", typeof options.coverageDisposition === "string", typeof options.coverageSkip === "string", options.close === true);
|
|
60003
|
+
const actionCount = countActions(wantsChanges, typeof options.context === "string", typeof options.sourceRefs === "string", typeof options.nodeCycle === "string", typeof options.draft === "string", typeof options.draftStatus === "string", typeof options.draftPatch === "string", typeof options.coverageDisposition === "string", typeof options.coverageSkip === "string", options.coverageSkipUnresolved === true, options.close === true);
|
|
59425
60004
|
if (actionCount !== 1) {
|
|
59426
|
-
throw new ContextError(ExitCode.UserError, "usage: context compile --scan-changes [--format json|table] [--ignore-source <source-id>...] | --context <slug> [--request-full-text <block-id>...] [--ignore-source <source-id>...] | --source-refs <slug> [--format json|table] | --node-cycle <slug> --input - --accept-safe-defaults [--format json|table] | --draft <slug> --input - --plan [--prepare] | --draft-status <slug> [--format json|table] | --draft-patch <slug> --input - --payload-digest <digest> --plan | --coverage-disposition - --coverage-disposition-node <slug> --payload-digest <digest> [--coverage-disposition-replace] | --coverage-skip <candidate-id> --coverage-disposition-node <slug> --payload-digest <digest> --reason <text> | --close", { category: ErrorCategory.UserInputInvalid });
|
|
60005
|
+
throw new ContextError(ExitCode.UserError, "usage: context compile --scan-changes [--format json|table] [--ignore-source <source-id>...] | --context <slug> [--request-full-text <block-id>...] [--ignore-source <source-id>...] | --source-refs <slug> [--format json|table] | --node-cycle <slug> --input - --accept-safe-defaults [--format json|table] | --draft <slug> --input - --plan [--prepare] | --draft-status <slug> [--format json|table] | --draft-patch <slug> --input - --payload-digest <digest> --plan | --coverage-disposition - --coverage-disposition-node <slug> --payload-digest <digest> [--coverage-disposition-replace] | --coverage-skip <candidate-id> --coverage-disposition-node <slug> --payload-digest <digest> --reason <text> | --coverage-skip-unresolved --coverage-disposition-node <slug> --payload-digest <digest> --reason <text> | --close", { category: ErrorCategory.UserInputInvalid });
|
|
59427
60006
|
}
|
|
59428
60007
|
const viewWasExplicit = typeof options.view === "string";
|
|
59429
60008
|
const view = compileOutputView(options.view);
|
|
@@ -59670,20 +60249,22 @@ function registerWorkflowCommands(program2) {
|
|
|
59670
60249
|
});
|
|
59671
60250
|
return;
|
|
59672
60251
|
}
|
|
59673
|
-
if (typeof options.coverageDisposition === "string" || typeof options.coverageSkip === "string") {
|
|
60252
|
+
if (typeof options.coverageDisposition === "string" || typeof options.coverageSkip === "string" || options.coverageSkipUnresolved === true) {
|
|
59674
60253
|
const currentRead = await readCurrentWorkflow(ctx.ctxDir);
|
|
59675
|
-
|
|
60254
|
+
const coverageSkipMode = options.coverageSkipUnresolved === true ? "bulk" : "single";
|
|
60255
|
+
const wantsCoverageSkip = typeof options.coverageSkip === "string" || options.coverageSkipUnresolved === true;
|
|
60256
|
+
if (wantsCoverageSkip && currentRead.status !== "ready") {
|
|
59676
60257
|
const missingFields = [
|
|
59677
60258
|
...typeof options.coverageDispositionNode === "string" ? [] : ["--coverage-disposition-node <slug>"],
|
|
59678
60259
|
...typeof options.payloadDigest === "string" ? [] : ["--payload-digest <digest>"],
|
|
59679
60260
|
...typeof options.reason === "string" && options.reason.trim().length > 0 ? [] : ["--reason <text>"]
|
|
59680
60261
|
];
|
|
59681
60262
|
if (missingFields.length > 0)
|
|
59682
|
-
throw coverageSkipMissingOptionsError(missingFields);
|
|
60263
|
+
throw coverageSkipMissingOptionsError(missingFields, coverageSkipMode);
|
|
59683
60264
|
}
|
|
59684
60265
|
const current = requireCurrentWorkflow(currentRead);
|
|
59685
|
-
if (
|
|
59686
|
-
throw coverageSkipMissingOptionsError(["--reason <text>"]);
|
|
60266
|
+
if (wantsCoverageSkip && (typeof options.reason !== "string" || options.reason.trim().length === 0)) {
|
|
60267
|
+
throw coverageSkipMissingOptionsError(["--reason <text>"], coverageSkipMode);
|
|
59687
60268
|
}
|
|
59688
60269
|
const requestedNode = typeof options.coverageDispositionNode === "string" ? options.coverageDispositionNode : undefined;
|
|
59689
60270
|
const requestedDigest = typeof options.payloadDigest === "string" ? options.payloadDigest : undefined;
|
|
@@ -59703,8 +60284,9 @@ function registerWorkflowCommands(program2) {
|
|
|
59703
60284
|
inferred.push(`payload digest: ${payload.digest}`);
|
|
59704
60285
|
const value = payload.value;
|
|
59705
60286
|
const candidates = Array.isArray(value.candidates) ? value.candidates : [];
|
|
59706
|
-
const patchInput = typeof options.coverageSkip === "string" ? coverageSkipPatch({ candidateId: options.coverageSkip, reason: String(options.reason).trim() }) : await readStructuredInput2(resolveStdinOnlyInput(options.coverageDisposition, "--coverage-disposition"));
|
|
60287
|
+
const patchInput = typeof options.coverageSkip === "string" ? coverageSkipPatch({ candidateId: options.coverageSkip, reason: String(options.reason).trim() }) : options.coverageSkipUnresolved === true ? coverageSkipUnresolvedPatch({ candidates, reason: String(options.reason).trim() }) : await readStructuredInput2(resolveStdinOnlyInput(options.coverageDisposition, "--coverage-disposition"));
|
|
59707
60288
|
const patch = await validateCoverageDispositions({ ctxDir: ctx.ctxDir, candidates, patch: patchInput });
|
|
60289
|
+
const skippedCount = patch.dispositions.filter((disposition2) => disposition2.action === "skip").length;
|
|
59708
60290
|
const status = await applyCoverageDispositionPatch({
|
|
59709
60291
|
ctxDir: ctx.ctxDir,
|
|
59710
60292
|
candidates,
|
|
@@ -59713,8 +60295,8 @@ function registerWorkflowCommands(program2) {
|
|
|
59713
60295
|
});
|
|
59714
60296
|
process.stdout.write(formatFeedback({
|
|
59715
60297
|
symbol: status.high_signal_unresolved > 0 ? "⚠" : "✓",
|
|
59716
|
-
action:
|
|
59717
|
-
subject: typeof options.coverageSkip === "string" ? `coverage candidate ${options.coverageSkip}` : `coverage disposition ${options.coverageDispositionNode}`,
|
|
60298
|
+
action: wantsCoverageSkip ? "skipped" : "applied",
|
|
60299
|
+
subject: typeof options.coverageSkip === "string" ? `coverage candidate ${options.coverageSkip}` : options.coverageSkipUnresolved === true ? `${skippedCount} unresolved coverage candidate(s) for ${value.node ?? scopeId}` : `coverage disposition ${options.coverageDispositionNode}`,
|
|
59718
60300
|
headline: formatCoverageSummary(status),
|
|
59719
60301
|
body: [
|
|
59720
60302
|
workflowSummaryLine(workflowState),
|
|
@@ -59903,7 +60485,7 @@ function registerWorkflowCommands(program2) {
|
|
|
59903
60485
|
init_cliFeedback();
|
|
59904
60486
|
init_errors();
|
|
59905
60487
|
import { lstat as lstat3, readdir as readdir23 } from "node:fs/promises";
|
|
59906
|
-
import { isAbsolute as isAbsolute7, join as
|
|
60488
|
+
import { isAbsolute as isAbsolute7, join as join58, relative as relative13, resolve as resolve11 } from "node:path";
|
|
59907
60489
|
|
|
59908
60490
|
// src/lib/pathFreeCommandMatrix.ts
|
|
59909
60491
|
var COMMAND_MATRIX = [
|
|
@@ -60180,12 +60762,12 @@ async function debugSourceRawPath(ctx, options) {
|
|
|
60180
60762
|
function workspaceStorageRoots(ctxDir) {
|
|
60181
60763
|
return {
|
|
60182
60764
|
ctxDir,
|
|
60183
|
-
raw:
|
|
60184
|
-
knowledge:
|
|
60185
|
-
output:
|
|
60186
|
-
archive:
|
|
60187
|
-
decisions:
|
|
60188
|
-
cache:
|
|
60765
|
+
raw: join58(ctxDir, "raw"),
|
|
60766
|
+
knowledge: join58(ctxDir, "knowledge"),
|
|
60767
|
+
output: join58(ctxDir, "output"),
|
|
60768
|
+
archive: join58(ctxDir, "archive"),
|
|
60769
|
+
decisions: join58(ctxDir, "decisions"),
|
|
60770
|
+
cache: join58(ctxDir, ".cache")
|
|
60189
60771
|
};
|
|
60190
60772
|
}
|
|
60191
60773
|
async function debugWorkspaceLocate(ctx, options) {
|
|
@@ -60275,7 +60857,7 @@ async function knowledgeNodeTypeDirs(ctxDir) {
|
|
|
60275
60857
|
async function inspectNodeObject(ctxDir, slug) {
|
|
60276
60858
|
const typeDirs = await knowledgeNodeTypeDirs(ctxDir);
|
|
60277
60859
|
const candidates = await Promise.all(typeDirs.map((type) => {
|
|
60278
|
-
const relPath =
|
|
60860
|
+
const relPath = join58("knowledge", type, `${slug}.md`);
|
|
60279
60861
|
return inspectWorkspacePath(ctxDir, relPath, relPath);
|
|
60280
60862
|
}));
|
|
60281
60863
|
return {
|
|
@@ -60291,7 +60873,7 @@ function safeArchiveSourceSegment(value) {
|
|
|
60291
60873
|
async function inspectArchiveObject(ctx, archiveId) {
|
|
60292
60874
|
const ctxDir = requireContext2(ctx, "debug storage inspect");
|
|
60293
60875
|
const source2 = (ctx.sources?.sources ?? []).find((entry) => entry.id === archiveId);
|
|
60294
|
-
const directRel =
|
|
60876
|
+
const directRel = join58("archive", "sources", safeArchiveSourceSegment(archiveId));
|
|
60295
60877
|
const paths = [
|
|
60296
60878
|
...source2?.archive_path !== undefined ? [await inspectWorkspacePath(ctxDir, source2.archive_path, "source.archive_path")] : [],
|
|
60297
60879
|
await inspectWorkspacePath(ctxDir, directRel, directRel)
|
|
@@ -60339,7 +60921,7 @@ async function walkForFileName(root, fileName, out2 = []) {
|
|
|
60339
60921
|
throw error;
|
|
60340
60922
|
}
|
|
60341
60923
|
for (const entry of entries) {
|
|
60342
|
-
const child =
|
|
60924
|
+
const child = join58(root, entry.name);
|
|
60343
60925
|
if (entry.isDirectory()) {
|
|
60344
60926
|
await walkForFileName(child, fileName, out2);
|
|
60345
60927
|
} else if (entry.isFile() && entry.name === fileName) {
|
|
@@ -60349,7 +60931,7 @@ async function walkForFileName(root, fileName, out2 = []) {
|
|
|
60349
60931
|
return out2;
|
|
60350
60932
|
}
|
|
60351
60933
|
async function inspectReviewObject(ctxDir, reviewId) {
|
|
60352
|
-
const matches = await walkForFileName(
|
|
60934
|
+
const matches = await walkForFileName(join58(ctxDir, "output", "workflows"), `review.${reviewId}.yaml`);
|
|
60353
60935
|
return {
|
|
60354
60936
|
object: `review:${reviewId}`,
|
|
60355
60937
|
kind: "review",
|
|
@@ -60359,14 +60941,14 @@ async function inspectReviewObject(ctxDir, reviewId) {
|
|
|
60359
60941
|
}
|
|
60360
60942
|
async function inspectWorkflowObject(ctxDir, workflowId) {
|
|
60361
60943
|
const current = await readCurrentWorkflow(ctxDir);
|
|
60362
|
-
const workflowRoot = workspacePath(ctxDir,
|
|
60944
|
+
const workflowRoot = workspacePath(ctxDir, join58("output", "workflows", safePathSegment2(workflowId)), "workflow output root");
|
|
60363
60945
|
const currentMatches = current.status === "ready" && current.state.workflow_id === workflowId;
|
|
60364
60946
|
return {
|
|
60365
60947
|
object: `workflow:${workflowId}`,
|
|
60366
60948
|
kind: "workflow",
|
|
60367
60949
|
paths: [
|
|
60368
60950
|
await inspectPath(workflowRoot, "workflow_output_root"),
|
|
60369
|
-
...currentMatches ? [await inspectWorkspacePath(ctxDir,
|
|
60951
|
+
...currentMatches ? [await inspectWorkspacePath(ctxDir, join58(".cache", "workflows", "current.yaml"), "current_workflow_state")] : []
|
|
60370
60952
|
],
|
|
60371
60953
|
current_workflow_state: current.status === "ready" ? {
|
|
60372
60954
|
status: currentMatches ? "matches-query" : "latest-singleton-different-workflow",
|
|
@@ -60513,10 +61095,10 @@ async function buildDebugSnapshot(ctx) {
|
|
|
60513
61095
|
manifest: cache.manifest
|
|
60514
61096
|
},
|
|
60515
61097
|
counts: {
|
|
60516
|
-
raw_entries: await countDirectEntries(
|
|
60517
|
-
knowledge_entries: await countDirectEntries(
|
|
60518
|
-
output_entries: await countDirectEntries(
|
|
60519
|
-
archive_entries: await countDirectEntries(
|
|
61098
|
+
raw_entries: await countDirectEntries(join58(ctxDir, "raw")),
|
|
61099
|
+
knowledge_entries: await countDirectEntries(join58(ctxDir, "knowledge")),
|
|
61100
|
+
output_entries: await countDirectEntries(join58(ctxDir, "output")),
|
|
61101
|
+
archive_entries: await countDirectEntries(join58(ctxDir, "archive"))
|
|
60520
61102
|
}
|
|
60521
61103
|
};
|
|
60522
61104
|
}
|
|
@@ -60590,11 +61172,11 @@ init_errors();
|
|
|
60590
61172
|
init_cliFeedback();
|
|
60591
61173
|
init_config();
|
|
60592
61174
|
init_errors();
|
|
60593
|
-
import { join as
|
|
61175
|
+
import { join as join62 } from "node:path";
|
|
60594
61176
|
|
|
60595
61177
|
// src/build/llms.ts
|
|
60596
61178
|
import { writeFile as writeFile24 } from "node:fs/promises";
|
|
60597
|
-
import { join as
|
|
61179
|
+
import { join as join60 } from "node:path";
|
|
60598
61180
|
|
|
60599
61181
|
// src/build/renderKnowledge.ts
|
|
60600
61182
|
init_nodeRenderer();
|
|
@@ -60677,7 +61259,7 @@ init_nodeRenderer();
|
|
|
60677
61259
|
init_exitCode();
|
|
60678
61260
|
init_knowledge();
|
|
60679
61261
|
import { mkdir as mkdir31 } from "node:fs/promises";
|
|
60680
|
-
import { join as
|
|
61262
|
+
import { join as join59 } from "node:path";
|
|
60681
61263
|
var MAX_TIMESTAMP_COLLISION_ATTEMPTS = 60;
|
|
60682
61264
|
var SUMMARY_MAX_LENGTH = 160;
|
|
60683
61265
|
var NODE_TYPE_ORDER = [NodeType2.domain, NodeType2.entity, NodeType2.action];
|
|
@@ -60704,11 +61286,11 @@ function isFsCode(error, code) {
|
|
|
60704
61286
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
60705
61287
|
}
|
|
60706
61288
|
async function createTimestampedPackageDir(outputRoot, packageName, now) {
|
|
60707
|
-
const packageRoot =
|
|
61289
|
+
const packageRoot = join59(outputRoot, packageName);
|
|
60708
61290
|
await mkdir31(packageRoot, { recursive: true });
|
|
60709
61291
|
for (let offsetSeconds = 0;offsetSeconds < MAX_TIMESTAMP_COLLISION_ATTEMPTS; offsetSeconds += 1) {
|
|
60710
61292
|
const candidateTime = new Date(now.getTime() + offsetSeconds * 1000);
|
|
60711
|
-
const candidate =
|
|
61293
|
+
const candidate = join59(packageRoot, formatBuildTimestamp(candidateTime));
|
|
60712
61294
|
try {
|
|
60713
61295
|
await mkdir31(candidate);
|
|
60714
61296
|
return candidate;
|
|
@@ -60784,10 +61366,10 @@ function renderLlmsIndex(input) {
|
|
|
60784
61366
|
}
|
|
60785
61367
|
async function writeLlmsPackage(input) {
|
|
60786
61368
|
const packageDir = await createTimestampedPackageDir(input.outputRoot, "llms-pkg", input.now);
|
|
60787
|
-
await writeFile24(
|
|
61369
|
+
await writeFile24(join60(packageDir, "llms.txt"), renderLlmsIndex(input), "utf8");
|
|
60788
61370
|
await Promise.all(input.nodes.map(async (node2) => {
|
|
60789
61371
|
const parsed = node2.located.parsed.node;
|
|
60790
|
-
await writeFile24(
|
|
61372
|
+
await writeFile24(join60(packageDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node2.activeSections }), "utf8");
|
|
60791
61373
|
}));
|
|
60792
61374
|
return {
|
|
60793
61375
|
packageDir,
|
|
@@ -60797,7 +61379,7 @@ async function writeLlmsPackage(input) {
|
|
|
60797
61379
|
|
|
60798
61380
|
// src/build/skillsPack.ts
|
|
60799
61381
|
import { mkdir as mkdir32, writeFile as writeFile25 } from "node:fs/promises";
|
|
60800
|
-
import { join as
|
|
61382
|
+
import { join as join61 } from "node:path";
|
|
60801
61383
|
var KNOWLEDGE_QUERY_PROCEDURE = [
|
|
60802
61384
|
"1. For a named topic, read the matching wiki file directly from `wikis/`.",
|
|
60803
61385
|
"2. For a broad question, grep `wikis/` with relevant keywords, then read the matching wiki files.",
|
|
@@ -60858,23 +61440,23 @@ function renderKnowledgeQuerySkill() {
|
|
|
60858
61440
|
}
|
|
60859
61441
|
async function writeSkillsPack(input) {
|
|
60860
61442
|
const packageDir = await createTimestampedPackageDir(input.outputRoot, "skills-pkg", input.now);
|
|
60861
|
-
const guidesDir =
|
|
60862
|
-
const skillsDir =
|
|
60863
|
-
const wikisDir =
|
|
61443
|
+
const guidesDir = join61(packageDir, "guides");
|
|
61444
|
+
const skillsDir = join61(packageDir, "skills");
|
|
61445
|
+
const wikisDir = join61(packageDir, "wikis");
|
|
60864
61446
|
await Promise.all([
|
|
60865
61447
|
mkdir32(guidesDir, { recursive: true }),
|
|
60866
61448
|
mkdir32(skillsDir, { recursive: true }),
|
|
60867
61449
|
mkdir32(wikisDir, { recursive: true }),
|
|
60868
|
-
mkdir32(
|
|
60869
|
-
mkdir32(
|
|
61450
|
+
mkdir32(join61(packageDir, "rules"), { recursive: true }),
|
|
61451
|
+
mkdir32(join61(packageDir, "integrations"), { recursive: true })
|
|
60870
61452
|
]);
|
|
60871
61453
|
await Promise.all([
|
|
60872
|
-
writeFile25(
|
|
60873
|
-
writeFile25(
|
|
61454
|
+
writeFile25(join61(guidesDir, "AGENTS.md"), renderAgentsGuide2(input), "utf8"),
|
|
61455
|
+
writeFile25(join61(skillsDir, "knowledge-query.md"), renderKnowledgeQuerySkill(), "utf8")
|
|
60874
61456
|
]);
|
|
60875
61457
|
await Promise.all(input.nodes.map(async (node2) => {
|
|
60876
61458
|
const parsed = node2.located.parsed.node;
|
|
60877
|
-
await writeFile25(
|
|
61459
|
+
await writeFile25(join61(wikisDir, buildNodeFileName(parsed)), renderExportNodeMarkdown({ node: parsed, sections: node2.activeSections }), "utf8");
|
|
60878
61460
|
}));
|
|
60879
61461
|
return {
|
|
60880
61462
|
packageDir,
|
|
@@ -60956,7 +61538,7 @@ async function collectExportNodes(ctxDir) {
|
|
|
60956
61538
|
return {
|
|
60957
61539
|
config,
|
|
60958
61540
|
nodes,
|
|
60959
|
-
outputRoot:
|
|
61541
|
+
outputRoot: join62(ctxDir, "output")
|
|
60960
61542
|
};
|
|
60961
61543
|
}
|
|
60962
61544
|
async function buildKnowledgePackage(input) {
|
|
@@ -61248,8 +61830,9 @@ function reconcileSchemaExample(name) {
|
|
|
61248
61830
|
"Do not use decisions: [] to accept all defaults; an empty decisions array means no decisions were made.",
|
|
61249
61831
|
"Keep proposed.content short and single-line; put long prose, code fences, and extended examples in proposed.detail.",
|
|
61250
61832
|
"Do not copy basis/evidence text into proposed.detail just to show the original raw text; source_ref/source_refs already provide traceability.",
|
|
61833
|
+
"For kind=example, preserving the cited fenced code/config/command block in proposed.detail is active example detail, not raw-evidence echo.",
|
|
61251
61834
|
'Use source_ref/source_refs copied from raw_snippets[].source_ref. Only run context source resolve-ref --node <slug> --text "<quote>" for manual recovery from a short quoted text fragment.',
|
|
61252
|
-
"Only set decided_by: user after an actual user or tester confirmed the answer.",
|
|
61835
|
+
"Only set decided_by: user after an actual user or tester confirmed the answer. Auto mode or broad permission to continue is not user confirmation; if no confirmation exists, fix/split evidence or leave ask_user unresolved.",
|
|
61253
61836
|
"When prepare/review returns agent_hints[], follow those result-specific hints before retrying."
|
|
61254
61837
|
],
|
|
61255
61838
|
example: {
|
|
@@ -61360,6 +61943,7 @@ function compileSchemaExample(name) {
|
|
|
61360
61943
|
"Prefer content_mode=extract when the cited raw sentence already fits as a short single-line claim. Use minimal_paraphrase only when direct extract would violate the content contract or when consolidating multiple spans; paraphrase_reason defaults to summary when omitted.",
|
|
61361
61944
|
"content must be a short single-line claim of 256 characters or fewer; put long prose, code fences, and extended examples in detail.",
|
|
61362
61945
|
"detail may contain active knowledge such as code/config/example material that cannot fit in content; do not use detail as a raw evidence copy or prefix it with labels such as 原文. source_refs already trace the evidence.",
|
|
61946
|
+
"For kind=example, preserving a cited fenced code/config/command block in detail is valid active example detail when the block is the knowledge users need to copy.",
|
|
61363
61947
|
"source_refs is the only compile-draft citation input. For a single citation, use an array with one source_ref string.",
|
|
61364
61948
|
"For dense raw material where one Section summarizes multiple contiguous blocks, provide all relevant source_refs. Contiguous means the selected source_refs resolve to consecutive evidence-manifest blocks from the same source, snapshot, and alias; if an intervening citation-eligible block is skipped, split the action or include that block's source_ref.",
|
|
61365
61949
|
"If NodeContext only contains navigation or placeholder evidence (Parent/Children/Related/Relations, no detailed content), use op: skip; do not create a low-value description Section from those lines.",
|
|
@@ -61445,7 +62029,8 @@ function compileSchemaExample(name) {
|
|
|
61445
62029
|
schema: "coverage-disposition",
|
|
61446
62030
|
schema_version: COVERAGE_DISPOSITION_SCHEMA_VERSION,
|
|
61447
62031
|
consumed_by: [
|
|
61448
|
-
"context compile --coverage-disposition - --coverage-disposition-node <slug> --payload-digest <digest>"
|
|
62032
|
+
"context compile --coverage-disposition - --coverage-disposition-node <slug> --payload-digest <digest>",
|
|
62033
|
+
'context compile --coverage-skip-unresolved --coverage-disposition-node <slug> --payload-digest <digest> --reason "<reason>"'
|
|
61449
62034
|
],
|
|
61450
62035
|
required: ["schema_version", "dispositions"],
|
|
61451
62036
|
enums: {
|
|
@@ -61455,7 +62040,8 @@ function compileSchemaExample(name) {
|
|
|
61455
62040
|
"Read node-scoped candidates with context workflow show --payload coverage-candidates --scope <node-run-scope> --view coverage.",
|
|
61456
62041
|
"Use new_section when the candidate source_refs were covered by new draft Sections.",
|
|
61457
62042
|
"Use merge_into_existing or covered_by_section only with target_section_id that mechanically covers the listed source_refs.",
|
|
61458
|
-
"Use skip with a reason when the candidate is intentionally not written for this node. For skip only, source_refs may be omitted to dispose the whole candidate."
|
|
62043
|
+
"Use skip with a reason when the candidate is intentionally not written for this node. For skip only, source_refs may be omitted to dispose the whole candidate.",
|
|
62044
|
+
"When every unresolved candidate in the node payload has the same skip rationale, prefer --coverage-skip-unresolved over hand-writing one disposition per candidate."
|
|
61459
62045
|
],
|
|
61460
62046
|
example: {
|
|
61461
62047
|
schema_version: COVERAGE_DISPOSITION_SCHEMA_VERSION,
|
|
@@ -61566,8 +62152,8 @@ function readPackageVersion() {
|
|
|
61566
62152
|
try {
|
|
61567
62153
|
let dir = dirname28(fileURLToPath4(import.meta.url));
|
|
61568
62154
|
for (let i = 0;i < 8; i++) {
|
|
61569
|
-
const pkg =
|
|
61570
|
-
if (
|
|
62155
|
+
const pkg = join63(dir, "package.json");
|
|
62156
|
+
if (existsSync49(pkg)) {
|
|
61571
62157
|
const parsed = JSON.parse(readFileSync3(pkg, "utf8"));
|
|
61572
62158
|
return parsed.version ?? "unknown";
|
|
61573
62159
|
}
|