snipara-companion 3.0.7 → 3.0.8
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/README.md +1 -1
- package/dist/index.js +495 -2
- package/docs/FULL_REFERENCE.md +11 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ These commands are useful without hosted Snipara:
|
|
|
43
43
|
| ------------------------------------------------------------ | ------------------------------------------------------------ |
|
|
44
44
|
| `source init` / `source sync` / `source status` | Local source snapshot, document preview, and code overlay |
|
|
45
45
|
| `impact` / `code impact` | File-level blast-radius from the local code overlay |
|
|
46
|
-
| `reality-check` | Intent
|
|
46
|
+
| `reality-check` | Intent Ledger, Unknown Registry, and verification checks |
|
|
47
47
|
| `code callers` / `imports` / `neighbors` / `shortest-path` | Structural repo questions from local files |
|
|
48
48
|
| `workflow start` / `phase-start` / `phase-commit` / `resume` | Agent continuity that survives compaction |
|
|
49
49
|
| `context-pack` | Reversible local packs for long logs, diffs, and tool output |
|
package/dist/index.js
CHANGED
|
@@ -19687,6 +19687,8 @@ var PROJECT_INTELLIGENCE_ENGINEERING_LEAD_PROOF_VERIFICATION_SOURCES = [
|
|
|
19687
19687
|
"unknown"
|
|
19688
19688
|
];
|
|
19689
19689
|
var PROJECT_REALITY_CHECK_VERSION = "project-reality-check-v0";
|
|
19690
|
+
var PROJECT_INTENT_LEDGER_VERSION = "intent-ledger-v1";
|
|
19691
|
+
var PROJECT_UNKNOWN_REGISTRY_VERSION = "unknown-registry-v1";
|
|
19690
19692
|
var SENSITIVE_SURFACES = [
|
|
19691
19693
|
{
|
|
19692
19694
|
key: "billing_atomicity",
|
|
@@ -19796,6 +19798,444 @@ function decisionText(decision) {
|
|
|
19796
19798
|
...decision.constraints ?? []
|
|
19797
19799
|
].filter(Boolean).join(" ");
|
|
19798
19800
|
}
|
|
19801
|
+
function confidenceBand(score) {
|
|
19802
|
+
if (score >= 0.8) return "high";
|
|
19803
|
+
if (score >= 0.55) return "medium";
|
|
19804
|
+
return "low";
|
|
19805
|
+
}
|
|
19806
|
+
function normalizeConfidenceScore(value, fallback) {
|
|
19807
|
+
if (typeof value !== "number" || Number.isNaN(value)) return fallback;
|
|
19808
|
+
return Math.max(0, Math.min(1, value));
|
|
19809
|
+
}
|
|
19810
|
+
function intentStatusFromDecision(decision) {
|
|
19811
|
+
const status = normalizeText(decision.status);
|
|
19812
|
+
if (/contradict|conflict/.test(status)) return "contradicted";
|
|
19813
|
+
if (/supersed|replac/.test(status)) return "superseded";
|
|
19814
|
+
if (/stale|expired|deprecated/.test(status)) return "stale";
|
|
19815
|
+
if (/approved|accepted|active|confirmed/.test(status)) return "approved";
|
|
19816
|
+
return "review_pending";
|
|
19817
|
+
}
|
|
19818
|
+
function intentConfidence(score, status, reasonCodes) {
|
|
19819
|
+
return {
|
|
19820
|
+
score,
|
|
19821
|
+
band: confidenceBand(score),
|
|
19822
|
+
state: status === "approved" ? "confirmed" : status === "stale" ? "stale" : status === "contradicted" ? "conflicted" : score < 0.55 ? "weak" : "review_pending",
|
|
19823
|
+
reasonCodes
|
|
19824
|
+
};
|
|
19825
|
+
}
|
|
19826
|
+
function stalenessFromStatus(status) {
|
|
19827
|
+
if (status === "stale" || status === "superseded") {
|
|
19828
|
+
return {
|
|
19829
|
+
state: "stale",
|
|
19830
|
+
reason: status === "superseded" ? "The source decision is marked as superseded." : "The source decision is marked as stale."
|
|
19831
|
+
};
|
|
19832
|
+
}
|
|
19833
|
+
if (status === "review_pending") {
|
|
19834
|
+
return {
|
|
19835
|
+
state: "unknown",
|
|
19836
|
+
reason: "The intent has not been reviewed as current project truth."
|
|
19837
|
+
};
|
|
19838
|
+
}
|
|
19839
|
+
return { state: "fresh", horizonDays: 90 };
|
|
19840
|
+
}
|
|
19841
|
+
function anchorMatchesFile(anchor, file) {
|
|
19842
|
+
const normalizedAnchor = anchor.replace(/^\.\//, "").trim();
|
|
19843
|
+
const normalizedFile = file.replace(/^\.\//, "").trim();
|
|
19844
|
+
return normalizedAnchor.length > 0 && normalizedFile.length > 0 && (normalizedFile.includes(normalizedAnchor) || normalizedAnchor.includes(normalizedFile));
|
|
19845
|
+
}
|
|
19846
|
+
function intentEntryMatchesFile(entry, file) {
|
|
19847
|
+
return entry.affectedAnchors.some((anchor) => anchorMatchesFile(anchor, file));
|
|
19848
|
+
}
|
|
19849
|
+
function inferDecisionAnchors(decision, changedFiles) {
|
|
19850
|
+
const explicitAnchors = uniqueStrings8(decision.affectedAnchors ?? []);
|
|
19851
|
+
if (explicitAnchors.length > 0) return explicitAnchors;
|
|
19852
|
+
const text = normalizeText(decisionText(decision));
|
|
19853
|
+
const inferred = SENSITIVE_SURFACES.flatMap(
|
|
19854
|
+
(surface) => matchesAnyText(text, surface.patterns) ? surfaceFiles(surface, changedFiles) : []
|
|
19855
|
+
);
|
|
19856
|
+
return uniqueStrings8(inferred);
|
|
19857
|
+
}
|
|
19858
|
+
function splitIntentPhrases(value) {
|
|
19859
|
+
if (!value) return [];
|
|
19860
|
+
return uniqueStrings8(
|
|
19861
|
+
value.split(/[;\n]/g).map((item) => item.replace(/^[-*]\s*/, "").trim()).filter(Boolean)
|
|
19862
|
+
).slice(0, 6);
|
|
19863
|
+
}
|
|
19864
|
+
function extractAntiGoals(text) {
|
|
19865
|
+
const matches = [...text.matchAll(/\b(?:avoid|forbid|prevent|do not|don't)\b[^.;\n]*/gi)].map(
|
|
19866
|
+
(match) => match[0].trim()
|
|
19867
|
+
);
|
|
19868
|
+
return uniqueStrings8(matches).slice(0, 5);
|
|
19869
|
+
}
|
|
19870
|
+
function extractRejectedAlternatives(text) {
|
|
19871
|
+
const matches = [
|
|
19872
|
+
...text.matchAll(/\b(?:rejected|instead of|rather than|not use|not using)\b[^.;\n]*/gi)
|
|
19873
|
+
].map((match) => match[0].trim());
|
|
19874
|
+
return uniqueStrings8(matches).slice(0, 5);
|
|
19875
|
+
}
|
|
19876
|
+
function documentLooksLikeIntent(doc) {
|
|
19877
|
+
const text = normalizeText([doc.path, doc.title, doc.contentPreview, doc.kind].join(" "));
|
|
19878
|
+
return /\b(adr|decision|intent|rationale|constraint|architecture|runbook)\b/.test(text);
|
|
19879
|
+
}
|
|
19880
|
+
function buildDecisionIntentEntry(decision, changedFiles) {
|
|
19881
|
+
const status = intentStatusFromDecision(decision);
|
|
19882
|
+
const anchors = inferDecisionAnchors(decision, changedFiles);
|
|
19883
|
+
const fullText = decisionText(decision);
|
|
19884
|
+
const confidenceScore = normalizeConfidenceScore(
|
|
19885
|
+
decision.confidenceScore,
|
|
19886
|
+
status === "approved" ? 0.86 : 0.66
|
|
19887
|
+
);
|
|
19888
|
+
const reasonCodes = uniqueStrings8([
|
|
19889
|
+
"decision_intent",
|
|
19890
|
+
status === "approved" ? "reviewed_source" : "review_needed",
|
|
19891
|
+
anchors.length > 0 ? "affected_anchors_present" : "affected_anchors_missing"
|
|
19892
|
+
]);
|
|
19893
|
+
return {
|
|
19894
|
+
id: `intent:${decision.id}`,
|
|
19895
|
+
title: decision.title,
|
|
19896
|
+
status,
|
|
19897
|
+
source: "decision",
|
|
19898
|
+
sourceDecisionId: decision.id,
|
|
19899
|
+
sourceDocumentPath: null,
|
|
19900
|
+
intent: {
|
|
19901
|
+
goal: decision.rationale || decision.decision || decision.title,
|
|
19902
|
+
constraints: uniqueStrings8([
|
|
19903
|
+
...decision.constraints ?? [],
|
|
19904
|
+
...splitIntentPhrases(decision.scope)
|
|
19905
|
+
]).slice(0, 8),
|
|
19906
|
+
antiGoals: extractAntiGoals(fullText),
|
|
19907
|
+
rejectedAlternatives: extractRejectedAlternatives(fullText)
|
|
19908
|
+
},
|
|
19909
|
+
affectedAnchors: anchors,
|
|
19910
|
+
owner: null,
|
|
19911
|
+
evidence: decision.evidence && decision.evidence.length > 0 ? decision.evidence : [
|
|
19912
|
+
{
|
|
19913
|
+
kind: "decision",
|
|
19914
|
+
label: `${decision.id}: ${decision.title}`,
|
|
19915
|
+
sourceRef: decision.id,
|
|
19916
|
+
strength: confidenceScore
|
|
19917
|
+
}
|
|
19918
|
+
],
|
|
19919
|
+
confidence: intentConfidence(confidenceScore, status, reasonCodes),
|
|
19920
|
+
staleness: stalenessFromStatus(status),
|
|
19921
|
+
reasonCodes,
|
|
19922
|
+
recommendedActions: status === "approved" ? ["Keep this intent linked when changing its affected anchors."] : ["Review this intent candidate before treating it as governing project truth."],
|
|
19923
|
+
caveats: [
|
|
19924
|
+
"Intent Ledger V1 preserves source-backed intent; it does not infer unstated architecture rules."
|
|
19925
|
+
]
|
|
19926
|
+
};
|
|
19927
|
+
}
|
|
19928
|
+
function buildDocumentIntentEntry(doc) {
|
|
19929
|
+
const preview4 = doc.contentPreview || doc.title || doc.path;
|
|
19930
|
+
const reasonCodes = ["document_intent_candidate", "review_needed"];
|
|
19931
|
+
const confidenceScore = doc.kind && /adr|decision/i.test(doc.kind) ? 0.66 : 0.58;
|
|
19932
|
+
return {
|
|
19933
|
+
id: `intent:doc:${doc.path}`,
|
|
19934
|
+
title: doc.title || doc.path,
|
|
19935
|
+
status: "review_pending",
|
|
19936
|
+
source: "document",
|
|
19937
|
+
sourceDecisionId: null,
|
|
19938
|
+
sourceDocumentPath: doc.path,
|
|
19939
|
+
intent: {
|
|
19940
|
+
goal: preview4,
|
|
19941
|
+
constraints: splitIntentPhrases(doc.contentPreview).slice(0, 4),
|
|
19942
|
+
antiGoals: extractAntiGoals(preview4),
|
|
19943
|
+
rejectedAlternatives: extractRejectedAlternatives(preview4)
|
|
19944
|
+
},
|
|
19945
|
+
affectedAnchors: [doc.path],
|
|
19946
|
+
owner: null,
|
|
19947
|
+
evidence: [
|
|
19948
|
+
{
|
|
19949
|
+
kind: "document",
|
|
19950
|
+
label: doc.title ? `${doc.path}: ${doc.title}` : doc.path,
|
|
19951
|
+
sourceRef: doc.sourceRef ?? doc.path,
|
|
19952
|
+
strength: confidenceScore
|
|
19953
|
+
}
|
|
19954
|
+
],
|
|
19955
|
+
confidence: intentConfidence(confidenceScore, "review_pending", reasonCodes),
|
|
19956
|
+
staleness: {
|
|
19957
|
+
state: "unknown",
|
|
19958
|
+
reason: "Document-derived intent candidates need review before becoming governing truth."
|
|
19959
|
+
},
|
|
19960
|
+
reasonCodes,
|
|
19961
|
+
recommendedActions: ["Promote, reject, or link this document intent candidate to a decision."],
|
|
19962
|
+
caveats: [
|
|
19963
|
+
"Document candidates are weaker than reviewed decisions until authority is confirmed."
|
|
19964
|
+
]
|
|
19965
|
+
};
|
|
19966
|
+
}
|
|
19967
|
+
function buildProjectIntentLedger(input, changedFilesOverride) {
|
|
19968
|
+
const changedFiles = uniqueStrings8(changedFilesOverride ?? input.changedFiles);
|
|
19969
|
+
const decisionEntries = (input.decisions ?? []).map(
|
|
19970
|
+
(decision) => buildDecisionIntentEntry(decision, changedFiles)
|
|
19971
|
+
);
|
|
19972
|
+
const decisionDocumentPaths = new Set(
|
|
19973
|
+
decisionEntries.flatMap(
|
|
19974
|
+
(entry) => entry.evidence.map((evidence) => evidence.sourceRef).filter(Boolean)
|
|
19975
|
+
)
|
|
19976
|
+
);
|
|
19977
|
+
const documentEntries = (input.documents ?? []).filter(documentLooksLikeIntent).filter((doc) => !decisionDocumentPaths.has(doc.path)).map(buildDocumentIntentEntry);
|
|
19978
|
+
const entries = [...decisionEntries, ...documentEntries];
|
|
19979
|
+
const linkedIntentIds = new Set(
|
|
19980
|
+
entries.filter((entry) => changedFiles.some((file) => intentEntryMatchesFile(entry, file))).map((entry) => entry.id)
|
|
19981
|
+
);
|
|
19982
|
+
const missingAnchors = changedFiles.filter(
|
|
19983
|
+
(file) => !entries.some((entry) => intentEntryMatchesFile(entry, file))
|
|
19984
|
+
);
|
|
19985
|
+
const coverage = changedFiles.length === 0 || entries.length > 0 && missingAnchors.length === 0 ? "linked" : entries.length === 0 ? "missing" : "partial";
|
|
19986
|
+
const reasonCodes = uniqueStrings8([
|
|
19987
|
+
coverage === "linked" ? "intent_coverage_linked" : void 0,
|
|
19988
|
+
coverage === "partial" ? "intent_coverage_partial" : void 0,
|
|
19989
|
+
coverage === "missing" ? "intent_coverage_missing" : void 0,
|
|
19990
|
+
entries.some((entry) => entry.status === "review_pending") ? "intent_review_pending" : void 0,
|
|
19991
|
+
entries.some((entry) => entry.staleness.state !== "fresh") ? "intent_freshness_unknown" : void 0
|
|
19992
|
+
]);
|
|
19993
|
+
return {
|
|
19994
|
+
version: PROJECT_INTENT_LEDGER_VERSION,
|
|
19995
|
+
coverage,
|
|
19996
|
+
totalIntentCount: entries.length,
|
|
19997
|
+
linkedIntentCount: linkedIntentIds.size,
|
|
19998
|
+
missingAnchorCount: missingAnchors.length,
|
|
19999
|
+
entries: entries.slice(0, 20),
|
|
20000
|
+
missingAnchors: missingAnchors.slice(0, 20),
|
|
20001
|
+
evidence: entries.flatMap((entry) => entry.evidence).slice(0, 10),
|
|
20002
|
+
reasonCodes,
|
|
20003
|
+
caveats: [
|
|
20004
|
+
"Intent Ledger V1 is a deterministic projection from supplied decisions and documents.",
|
|
20005
|
+
coverage === "missing" ? "Missing intent means no linked source was supplied for this scope, not that no governing intent exists." : void 0
|
|
20006
|
+
].filter((item) => Boolean(item))
|
|
20007
|
+
};
|
|
20008
|
+
}
|
|
20009
|
+
function unknownId(category, key, scope) {
|
|
20010
|
+
return makeFindingId("decision_vs_code", `${category}:${key}`, scope).replace(
|
|
20011
|
+
"reality:decision-vs-code",
|
|
20012
|
+
"unknown"
|
|
20013
|
+
);
|
|
20014
|
+
}
|
|
20015
|
+
function unknownStatusFromSeverity(unknowns) {
|
|
20016
|
+
if (unknowns.some(
|
|
20017
|
+
(unknown) => unknown.severity === "blocking" || unknown.severity === "review_required"
|
|
20018
|
+
)) {
|
|
20019
|
+
return "risk";
|
|
20020
|
+
}
|
|
20021
|
+
if (unknowns.some((unknown) => unknown.severity === "watch")) return "watch";
|
|
20022
|
+
return "clear";
|
|
20023
|
+
}
|
|
20024
|
+
function unknownConfidence(score, state, reasonCodes) {
|
|
20025
|
+
return {
|
|
20026
|
+
score,
|
|
20027
|
+
band: confidenceBand(score),
|
|
20028
|
+
state,
|
|
20029
|
+
reasonCodes
|
|
20030
|
+
};
|
|
20031
|
+
}
|
|
20032
|
+
function makeUnknown(input) {
|
|
20033
|
+
return {
|
|
20034
|
+
id: unknownId(input.category, input.title, input.scope),
|
|
20035
|
+
category: input.category,
|
|
20036
|
+
severity: input.severity,
|
|
20037
|
+
title: input.title,
|
|
20038
|
+
summary: input.summary,
|
|
20039
|
+
scope: input.scope.slice(0, 12),
|
|
20040
|
+
ownerCandidate: input.ownerCandidate ?? null,
|
|
20041
|
+
confidence: unknownConfidence(
|
|
20042
|
+
input.confidenceScore,
|
|
20043
|
+
input.confidenceState ?? "review_pending",
|
|
20044
|
+
input.reasonCodes
|
|
20045
|
+
),
|
|
20046
|
+
linkedFindingIds: input.linkedFindingIds ?? [],
|
|
20047
|
+
linkedIntentIds: input.linkedIntentIds ?? [],
|
|
20048
|
+
evidence: (input.evidence ?? []).slice(0, 8),
|
|
20049
|
+
reasonCodes: input.reasonCodes,
|
|
20050
|
+
resolutionActions: input.resolutionActions,
|
|
20051
|
+
closureEvidenceRequired: input.closureEvidenceRequired,
|
|
20052
|
+
caveats: input.caveats ?? []
|
|
20053
|
+
};
|
|
20054
|
+
}
|
|
20055
|
+
function buildProjectUnknownRegistry(input) {
|
|
20056
|
+
const unknowns = [];
|
|
20057
|
+
if (input.intentLedger.missingAnchors.length > 0) {
|
|
20058
|
+
unknowns.push(
|
|
20059
|
+
makeUnknown({
|
|
20060
|
+
category: "missing_intent",
|
|
20061
|
+
severity: input.findings.some((finding) => finding.severity === "review_required") ? "review_required" : "watch",
|
|
20062
|
+
title: "Governing intent is not linked for part of this scope",
|
|
20063
|
+
summary: "At least one changed anchor has no linked reviewed decision or document intent. This is an unknown, not proof that no constraint exists.",
|
|
20064
|
+
scope: input.intentLedger.missingAnchors,
|
|
20065
|
+
confidenceScore: 0.86,
|
|
20066
|
+
linkedFindingIds: input.findings.filter((finding) => finding.reasonCodes.includes("missing_intent")).map((finding) => finding.id),
|
|
20067
|
+
evidence: input.intentLedger.evidence,
|
|
20068
|
+
reasonCodes: ["missing_intent", "unknown_governing_decision"],
|
|
20069
|
+
resolutionActions: [
|
|
20070
|
+
"Link an existing decision, approve a review-pending intent, or create a new decision candidate."
|
|
20071
|
+
],
|
|
20072
|
+
closureEvidenceRequired: [
|
|
20073
|
+
"Reviewed decision, ADR, issue, PR Answer Pack, or final workflow commit linked to the changed anchor."
|
|
20074
|
+
],
|
|
20075
|
+
caveats: [
|
|
20076
|
+
"Retrieval/index freshness can make linked intent appear missing until context is refreshed."
|
|
20077
|
+
]
|
|
20078
|
+
})
|
|
20079
|
+
);
|
|
20080
|
+
}
|
|
20081
|
+
for (const entry of input.intentLedger.entries) {
|
|
20082
|
+
if (entry.status === "review_pending") {
|
|
20083
|
+
unknowns.push(
|
|
20084
|
+
makeUnknown({
|
|
20085
|
+
category: "intent_review_pending",
|
|
20086
|
+
severity: "watch",
|
|
20087
|
+
title: `Intent needs review: ${entry.title}`,
|
|
20088
|
+
summary: "A source-backed intent candidate exists, but it is not reviewed enough to act as governing project truth.",
|
|
20089
|
+
scope: entry.affectedAnchors,
|
|
20090
|
+
confidenceScore: entry.confidence.score,
|
|
20091
|
+
linkedIntentIds: [entry.id],
|
|
20092
|
+
evidence: entry.evidence,
|
|
20093
|
+
reasonCodes: ["intent_review_pending"],
|
|
20094
|
+
resolutionActions: ["Approve, reject, supersede, or link this intent candidate."],
|
|
20095
|
+
closureEvidenceRequired: ["Review receipt or approved ProjectDecision authority state."],
|
|
20096
|
+
caveats: entry.caveats
|
|
20097
|
+
})
|
|
20098
|
+
);
|
|
20099
|
+
}
|
|
20100
|
+
if (entry.status === "stale" || entry.status === "superseded") {
|
|
20101
|
+
unknowns.push(
|
|
20102
|
+
makeUnknown({
|
|
20103
|
+
category: "stale_intent",
|
|
20104
|
+
severity: "review_required",
|
|
20105
|
+
title: `Intent may be stale: ${entry.title}`,
|
|
20106
|
+
summary: "A linked intent is stale or superseded; current implementation may no longer match the original rationale.",
|
|
20107
|
+
scope: entry.affectedAnchors,
|
|
20108
|
+
confidenceScore: 0.78,
|
|
20109
|
+
confidenceState: "stale",
|
|
20110
|
+
linkedIntentIds: [entry.id],
|
|
20111
|
+
evidence: entry.evidence,
|
|
20112
|
+
reasonCodes: ["stale_intent", entry.status],
|
|
20113
|
+
resolutionActions: [
|
|
20114
|
+
"Refresh, supersede, or archive the stale intent before relying on it."
|
|
20115
|
+
],
|
|
20116
|
+
closureEvidenceRequired: ["Updated decision or explicit supersession evidence."],
|
|
20117
|
+
caveats: entry.caveats
|
|
20118
|
+
})
|
|
20119
|
+
);
|
|
20120
|
+
}
|
|
20121
|
+
}
|
|
20122
|
+
for (const finding of input.findings) {
|
|
20123
|
+
if (finding.reasonCodes.includes("verification_missing")) {
|
|
20124
|
+
unknowns.push(
|
|
20125
|
+
makeUnknown({
|
|
20126
|
+
category: "missing_verification",
|
|
20127
|
+
severity: finding.severity,
|
|
20128
|
+
title: `Verification gap: ${finding.title}`,
|
|
20129
|
+
summary: "The change touches a sensitive surface but no matching verification evidence was supplied.",
|
|
20130
|
+
scope: finding.changedFiles,
|
|
20131
|
+
confidenceScore: 0.82,
|
|
20132
|
+
linkedFindingIds: [finding.id],
|
|
20133
|
+
linkedIntentIds: finding.decisionIds.map((id) => `intent:${id}`),
|
|
20134
|
+
evidence: finding.evidence,
|
|
20135
|
+
reasonCodes: ["missing_verification", ...finding.reasonCodes],
|
|
20136
|
+
resolutionActions: finding.recommendedActions,
|
|
20137
|
+
closureEvidenceRequired: [
|
|
20138
|
+
"Focused test, smoke check, review proof, or verification checklist item linked to the changed behavior."
|
|
20139
|
+
],
|
|
20140
|
+
caveats: finding.caveats
|
|
20141
|
+
})
|
|
20142
|
+
);
|
|
20143
|
+
}
|
|
20144
|
+
if (finding.type === "architecture_drift") {
|
|
20145
|
+
unknowns.push(
|
|
20146
|
+
makeUnknown({
|
|
20147
|
+
category: "architecture_drift",
|
|
20148
|
+
severity: finding.severity,
|
|
20149
|
+
title: finding.title,
|
|
20150
|
+
summary: "A boundary-spanning change needs architecture-intent confirmation before treating the current dependency shape as intended.",
|
|
20151
|
+
scope: finding.changedFiles,
|
|
20152
|
+
confidenceScore: 0.72,
|
|
20153
|
+
linkedFindingIds: [finding.id],
|
|
20154
|
+
evidence: finding.evidence,
|
|
20155
|
+
reasonCodes: ["architecture_drift_unknown", ...finding.reasonCodes],
|
|
20156
|
+
resolutionActions: finding.recommendedActions,
|
|
20157
|
+
closureEvidenceRequired: [
|
|
20158
|
+
"Architecture rule, reviewed decision, or code graph evidence proving the boundary is intentional."
|
|
20159
|
+
],
|
|
20160
|
+
caveats: finding.caveats
|
|
20161
|
+
})
|
|
20162
|
+
);
|
|
20163
|
+
}
|
|
20164
|
+
if (finding.reasonCodes.includes("risk_term_in_diff") || finding.reasonCodes.includes("no_linked_decision") && finding.severity === "review_required") {
|
|
20165
|
+
unknowns.push(
|
|
20166
|
+
makeUnknown({
|
|
20167
|
+
category: "heuristic_calibration",
|
|
20168
|
+
severity: "watch",
|
|
20169
|
+
title: `Heuristic signal needs calibration: ${finding.title}`,
|
|
20170
|
+
summary: "Reality Check V1 used regex/path/free-text signals for this finding. Keep it advisory or narrow-scope until project-specific thresholds are calibrated.",
|
|
20171
|
+
scope: finding.changedFiles,
|
|
20172
|
+
confidenceScore: 0.68,
|
|
20173
|
+
confidenceState: "weak",
|
|
20174
|
+
linkedFindingIds: [finding.id],
|
|
20175
|
+
evidence: finding.evidence,
|
|
20176
|
+
reasonCodes: ["heuristic_calibration_needed", ...finding.reasonCodes],
|
|
20177
|
+
resolutionActions: [
|
|
20178
|
+
"Confirm the finding against structured intent, tests, or reviewed project-specific rules before broad CI gating."
|
|
20179
|
+
],
|
|
20180
|
+
closureEvidenceRequired: [
|
|
20181
|
+
"Reviewed calibration decision or false-positive/true-positive outcome history for this surface."
|
|
20182
|
+
],
|
|
20183
|
+
caveats: [
|
|
20184
|
+
"--enforce should remain opt-in for this finding family until calibration evidence exists."
|
|
20185
|
+
]
|
|
20186
|
+
})
|
|
20187
|
+
);
|
|
20188
|
+
}
|
|
20189
|
+
}
|
|
20190
|
+
if (input.dirtyFiles.length > 0) {
|
|
20191
|
+
unknowns.push(
|
|
20192
|
+
makeUnknown({
|
|
20193
|
+
category: "dirty_local_evidence",
|
|
20194
|
+
severity: "watch",
|
|
20195
|
+
title: "Local dirty evidence is not hosted truth",
|
|
20196
|
+
summary: "The local scan included dirty files that hosted PR checks and code graph indexes cannot see until committed.",
|
|
20197
|
+
scope: input.dirtyFiles,
|
|
20198
|
+
confidenceScore: 0.9,
|
|
20199
|
+
linkedFindingIds: input.findings.filter((finding) => finding.reasonCodes.includes("dirty_working_tree")).map((finding) => finding.id),
|
|
20200
|
+
evidence: input.dirtyFiles.map((file) => ({
|
|
20201
|
+
kind: "repository",
|
|
20202
|
+
label: file,
|
|
20203
|
+
sourceRef: file,
|
|
20204
|
+
strength: 0.5
|
|
20205
|
+
})),
|
|
20206
|
+
reasonCodes: ["dirty_local_evidence", "hosted_context_gap"],
|
|
20207
|
+
resolutionActions: [
|
|
20208
|
+
"Commit, stash, or exclude dirty files before relying on hosted checks."
|
|
20209
|
+
],
|
|
20210
|
+
closureEvidenceRequired: ["Clean git status or explicit review of dirty files."],
|
|
20211
|
+
caveats: ["Dirty-file unknowns are local-only and should not be treated as PR evidence."]
|
|
20212
|
+
})
|
|
20213
|
+
);
|
|
20214
|
+
}
|
|
20215
|
+
const deduped = Array.from(
|
|
20216
|
+
new Map(unknowns.map((unknown) => [unknown.id, unknown])).values()
|
|
20217
|
+
).slice(0, 24);
|
|
20218
|
+
const status = unknownStatusFromSeverity(deduped);
|
|
20219
|
+
return {
|
|
20220
|
+
version: PROJECT_UNKNOWN_REGISTRY_VERSION,
|
|
20221
|
+
status,
|
|
20222
|
+
unknownCount: deduped.length,
|
|
20223
|
+
riskCount: deduped.filter(
|
|
20224
|
+
(unknown) => unknown.severity === "review_required" || unknown.severity === "blocking"
|
|
20225
|
+
).length,
|
|
20226
|
+
watchCount: deduped.filter((unknown) => unknown.severity === "watch").length,
|
|
20227
|
+
categories: uniqueStrings8(
|
|
20228
|
+
deduped.map((unknown) => unknown.category)
|
|
20229
|
+
),
|
|
20230
|
+
unknowns: deduped,
|
|
20231
|
+
evidence: deduped.flatMap((unknown) => unknown.evidence).slice(0, 12),
|
|
20232
|
+
reasonCodes: uniqueStrings8(deduped.flatMap((unknown) => unknown.reasonCodes)).slice(0, 24),
|
|
20233
|
+
caveats: [
|
|
20234
|
+
"Unknown Registry V1 records evidence-backed gaps; closing an unknown requires source-backed evidence.",
|
|
20235
|
+
deduped.some((unknown) => unknown.category === "heuristic_calibration") ? "Heuristic calibration unknowns prevent regex/path findings from masquerading as fully calibrated truth." : void 0
|
|
20236
|
+
].filter((item) => Boolean(item))
|
|
20237
|
+
};
|
|
20238
|
+
}
|
|
19799
20239
|
function surfaceFiles(surface, changedFiles) {
|
|
19800
20240
|
return changedFiles.filter((file) => surface.patterns.some((pattern) => pattern.test(file)));
|
|
19801
20241
|
}
|
|
@@ -19836,6 +20276,7 @@ function buildProjectRealityCheck(input) {
|
|
|
19836
20276
|
const findings = [];
|
|
19837
20277
|
const decisions = input.decisions ?? [];
|
|
19838
20278
|
const contextEvidence2 = [...docEvidence(input), ...symbolEvidence(input)];
|
|
20279
|
+
const intentLedger = buildProjectIntentLedger(input, changedFiles);
|
|
19839
20280
|
for (const surface of SENSITIVE_SURFACES) {
|
|
19840
20281
|
const files = surfaceFiles(surface, changedFiles);
|
|
19841
20282
|
if (files.length === 0) continue;
|
|
@@ -19896,7 +20337,7 @@ function buildProjectRealityCheck(input) {
|
|
|
19896
20337
|
]
|
|
19897
20338
|
});
|
|
19898
20339
|
}
|
|
19899
|
-
if (changedFiles.length > 0 &&
|
|
20340
|
+
if (changedFiles.length > 0 && intentLedger.linkedIntentCount === 0 && (input.source === "pull_request" || findings.length > 0)) {
|
|
19900
20341
|
findings.push({
|
|
19901
20342
|
id: makeFindingId("decision_vs_code", "missing_intent", changedFiles),
|
|
19902
20343
|
type: "decision_vs_code",
|
|
@@ -19961,6 +20402,13 @@ function buildProjectRealityCheck(input) {
|
|
|
19961
20402
|
0,
|
|
19962
20403
|
20
|
|
19963
20404
|
);
|
|
20405
|
+
const unknownRegistry = buildProjectUnknownRegistry({
|
|
20406
|
+
realityCheck: input,
|
|
20407
|
+
changedFiles,
|
|
20408
|
+
dirtyFiles,
|
|
20409
|
+
findings,
|
|
20410
|
+
intentLedger
|
|
20411
|
+
});
|
|
19964
20412
|
const caveats = uniqueStrings8([
|
|
19965
20413
|
"Project Reality Check V0 is advisory and evidence-backed; it is not deterministic repository simulation.",
|
|
19966
20414
|
input.source === "local" ? "Local results may include dirty files that hosted PR checks cannot see." : "PR results are scoped to GitHub-provided PR files and indexed Snipara context."
|
|
@@ -19975,9 +20423,15 @@ function buildProjectRealityCheck(input) {
|
|
|
19975
20423
|
changedFileCount: changedFiles.length,
|
|
19976
20424
|
findingCount: findings.length,
|
|
19977
20425
|
findings,
|
|
20426
|
+
intentLedger,
|
|
20427
|
+
unknownRegistry,
|
|
19978
20428
|
requiredActions,
|
|
19979
20429
|
watchItems,
|
|
19980
|
-
reasonCodes
|
|
20430
|
+
reasonCodes: uniqueStrings8([
|
|
20431
|
+
...reasonCodes,
|
|
20432
|
+
...intentLedger.reasonCodes,
|
|
20433
|
+
...unknownRegistry.reasonCodes
|
|
20434
|
+
]).slice(0, 30),
|
|
19981
20435
|
caveats
|
|
19982
20436
|
};
|
|
19983
20437
|
}
|
|
@@ -19987,6 +20441,45 @@ function renderProjectRealityCheckMarkdown(result) {
|
|
|
19987
20441
|
`- Scope: ${result.changedFileCount} changed file(s), ${result.findingCount} finding(s).`,
|
|
19988
20442
|
`- Summary: ${result.summary}`
|
|
19989
20443
|
];
|
|
20444
|
+
lines.push(
|
|
20445
|
+
"",
|
|
20446
|
+
"Intent Ledger:",
|
|
20447
|
+
`- Coverage: ${result.intentLedger.coverage}; ${result.intentLedger.linkedIntentCount}/${result.intentLedger.totalIntentCount} intent(s) linked to this scope.`
|
|
20448
|
+
);
|
|
20449
|
+
for (const entry of result.intentLedger.entries.slice(0, 5)) {
|
|
20450
|
+
lines.push(
|
|
20451
|
+
`- ${entry.status.toUpperCase()} ${entry.id}: ${entry.title}`,
|
|
20452
|
+
` Intent: ${entry.intent.goal}`,
|
|
20453
|
+
` Confidence: ${entry.confidence.band} (${Math.round(entry.confidence.score * 100)}%).`
|
|
20454
|
+
);
|
|
20455
|
+
if (entry.affectedAnchors.length > 0) {
|
|
20456
|
+
lines.push(` Anchors: ${entry.affectedAnchors.slice(0, 5).join(", ")}`);
|
|
20457
|
+
}
|
|
20458
|
+
}
|
|
20459
|
+
if (result.intentLedger.missingAnchors.length > 0) {
|
|
20460
|
+
lines.push(
|
|
20461
|
+
`- Missing linked intent for: ${result.intentLedger.missingAnchors.slice(0, 8).map((file) => `\`${file}\``).join(", ")}`
|
|
20462
|
+
);
|
|
20463
|
+
}
|
|
20464
|
+
lines.push(
|
|
20465
|
+
"",
|
|
20466
|
+
"Unknown Registry:",
|
|
20467
|
+
`- Status: ${result.unknownRegistry.status}; ${result.unknownRegistry.unknownCount} unknown(s), ${result.unknownRegistry.riskCount} risk item(s).`
|
|
20468
|
+
);
|
|
20469
|
+
for (const unknown of result.unknownRegistry.unknowns.slice(0, 6)) {
|
|
20470
|
+
lines.push(
|
|
20471
|
+
`- ${unknown.severity.toUpperCase()} ${unknown.category}: ${unknown.title}`,
|
|
20472
|
+
` ${unknown.summary}`
|
|
20473
|
+
);
|
|
20474
|
+
if (unknown.scope.length > 0) {
|
|
20475
|
+
lines.push(
|
|
20476
|
+
` Scope: ${unknown.scope.slice(0, 5).map((item) => `\`${item}\``).join(", ")}`
|
|
20477
|
+
);
|
|
20478
|
+
}
|
|
20479
|
+
if (unknown.resolutionActions.length > 0) {
|
|
20480
|
+
lines.push(` Resolve: ${unknown.resolutionActions[0]}`);
|
|
20481
|
+
}
|
|
20482
|
+
}
|
|
19990
20483
|
if (result.findings.length > 0) {
|
|
19991
20484
|
lines.push("", "Findings:");
|
|
19992
20485
|
for (const finding of result.findings.slice(0, 8)) {
|
package/docs/FULL_REFERENCE.md
CHANGED
|
@@ -195,8 +195,13 @@ snipara-companion workflow resume --include-session-context
|
|
|
195
195
|
- `brief` is the short alias for `intelligence brief`.
|
|
196
196
|
- `reality-check` compares changed files, linked intent, context docs, symbols,
|
|
197
197
|
and verification hints to flag contradiction-to-reality risks before commit or
|
|
198
|
-
merge.
|
|
199
|
-
|
|
198
|
+
merge. Output includes an Intent Ledger section with source-backed intent
|
|
199
|
+
coverage, confidence, affected anchors, and missing intent anchors, plus an
|
|
200
|
+
Unknown Registry section that ranks missing intent, missing verification,
|
|
201
|
+
dirty local evidence, architecture drift, stale/review-pending intent, and
|
|
202
|
+
heuristic-calibration gaps. Keep `--enforce` opt-in for narrow hooks or
|
|
203
|
+
calibrated CI adapters; the V1 heuristics are advisory and can produce false
|
|
204
|
+
positives on broad text/path matches.
|
|
200
205
|
- `timeline` is the Git-style log for workflow starts, phase starts, phase
|
|
201
206
|
commits, final commits, and Team Sync handoffs.
|
|
202
207
|
- `workflow impact-gate` audits committed local workflow phases that are ahead
|
|
@@ -951,7 +956,9 @@ snipara-companion reality-check \
|
|
|
951
956
|
The command reads the local Git scope by default, includes dirty files unless
|
|
952
957
|
`--no-include-dirty` is set, and also accepts explicit `--changed-files` for
|
|
953
958
|
hooks or CI collectors. `--enforce` exits non-zero for `review_required` or
|
|
954
|
-
`blocking` findings
|
|
959
|
+
`blocking` findings, but should stay opt-in until the matched surfaces,
|
|
960
|
+
verification signals, and project-specific thresholds have been calibrated.
|
|
961
|
+
JSON output is available with `--json`.
|
|
955
962
|
|
|
956
963
|
Use `intelligence ledger-export` when an agent run, review, or replay benchmark
|
|
957
964
|
needs a portable Coding Intelligence Ledger instead of a raw transcript:
|
|
@@ -1028,7 +1035,7 @@ Semantics:
|
|
|
1028
1035
|
- `snipara-companion status` = top-level agentic work status across local workflow state, git dirtiness, and Team Sync carryover
|
|
1029
1036
|
- `snipara-companion source init|sync|status|snapshot|watch` = automatic local source activation for folders with or without Git metadata; writes `.snipara/source/latest.json`, previews document sync, and refreshes the local code overlay cache
|
|
1030
1037
|
- `snipara-companion brief` = short alias for `snipara-companion intelligence brief`
|
|
1031
|
-
- `snipara-companion reality-check` = local Project Reality Check for supplied or Git-derived changed files; `--enforce`
|
|
1038
|
+
- `snipara-companion reality-check` = local Project Reality Check plus Intent Ledger and Unknown Registry coverage for supplied or Git-derived changed files; `--enforce` is an opt-in strict mode for calibrated hooks
|
|
1032
1039
|
- `snipara-companion timeline` = local timeline of workflow starts, phase starts, phase commits, final commits, and Team Sync handoffs
|
|
1033
1040
|
- `snipara-companion handoff` = top-level agent-ready Markdown/JSON handoff artifact plus the same local/hosted Team Sync handoff persistence
|
|
1034
1041
|
- `snipara-companion intelligence brief` = one local Project Intelligence brief that combines resume context, memory health, and code impact for a task
|