snipara-companion 3.0.7 → 3.0.9
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 +535 -18
- 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
|
@@ -19612,7 +19612,7 @@ async function agentReadinessAuditCommand(options) {
|
|
|
19612
19612
|
var fs21 = __toESM(require("fs"));
|
|
19613
19613
|
var path20 = __toESM(require("path"));
|
|
19614
19614
|
|
|
19615
|
-
// ../project-intelligence-contracts/src/
|
|
19615
|
+
// ../project-intelligence-contracts/src/engineering-lead.ts
|
|
19616
19616
|
var PROJECT_HEALTH_COCKPIT_STATUSES = ["healthy", "watch", "risk", "unknown"];
|
|
19617
19617
|
var PROJECT_INTELLIGENCE_ENGINEERING_LEAD_PLAN_VERSION = "project-intelligence-engineering-lead-plan-v0";
|
|
19618
19618
|
var PROJECT_INTELLIGENCE_ENGINEERING_LEAD_CONTRACT_VERSION = "engineering-lead-contract-v1";
|
|
@@ -19686,7 +19686,8 @@ var PROJECT_INTELLIGENCE_ENGINEERING_LEAD_PROOF_VERIFICATION_SOURCES = [
|
|
|
19686
19686
|
"manual",
|
|
19687
19687
|
"unknown"
|
|
19688
19688
|
];
|
|
19689
|
-
|
|
19689
|
+
|
|
19690
|
+
// ../project-intelligence-contracts/src/shared.ts
|
|
19690
19691
|
var SENSITIVE_SURFACES = [
|
|
19691
19692
|
{
|
|
19692
19693
|
key: "billing_atomicity",
|
|
@@ -19762,23 +19763,11 @@ function hasPattern(values, patterns) {
|
|
|
19762
19763
|
function matchesAnyText(text, patterns) {
|
|
19763
19764
|
return patterns.some((pattern) => pattern.test(text));
|
|
19764
19765
|
}
|
|
19765
|
-
function
|
|
19766
|
-
if (severity === "blocking") return 0;
|
|
19767
|
-
if (severity === "review_required") return 30;
|
|
19768
|
-
if (severity === "watch") return 70;
|
|
19769
|
-
return 90;
|
|
19770
|
-
}
|
|
19771
|
-
function resultStatusFromFindings(findings) {
|
|
19772
|
-
if (findings.some((finding) => finding.severity === "blocking")) return "blocking";
|
|
19773
|
-
if (findings.some((finding) => finding.severity === "review_required")) return "review_required";
|
|
19774
|
-
if (findings.some((finding) => finding.severity === "watch")) return "advisory";
|
|
19775
|
-
return "pass";
|
|
19776
|
-
}
|
|
19777
|
-
function makeFindingId(type, key, files) {
|
|
19766
|
+
function makeScopedId(prefix, type, key, files) {
|
|
19778
19767
|
const compactFiles = files.slice(0, 3).map(
|
|
19779
19768
|
(file) => file.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "").toLowerCase()
|
|
19780
19769
|
).filter(Boolean).join("-");
|
|
19781
|
-
return [
|
|
19770
|
+
return [prefix, type, key, compactFiles || "project"].join(":").slice(0, 180);
|
|
19782
19771
|
}
|
|
19783
19772
|
function decisionMatchesChangedFiles(decision, changedFiles) {
|
|
19784
19773
|
const anchors = decision.affectedAnchors ?? [];
|
|
@@ -19796,9 +19785,484 @@ function decisionText(decision) {
|
|
|
19796
19785
|
...decision.constraints ?? []
|
|
19797
19786
|
].filter(Boolean).join(" ");
|
|
19798
19787
|
}
|
|
19788
|
+
function confidenceBand(score) {
|
|
19789
|
+
if (score >= 0.8) return "high";
|
|
19790
|
+
if (score >= 0.55) return "medium";
|
|
19791
|
+
return "low";
|
|
19792
|
+
}
|
|
19793
|
+
function normalizeConfidenceScore(value, fallback) {
|
|
19794
|
+
if (typeof value !== "number" || Number.isNaN(value)) return fallback;
|
|
19795
|
+
return Math.max(0, Math.min(1, value));
|
|
19796
|
+
}
|
|
19797
|
+
function anchorMatchesFile(anchor, file) {
|
|
19798
|
+
const normalizedAnchor = anchor.replace(/^\.\//, "").trim();
|
|
19799
|
+
const normalizedFile = file.replace(/^\.\//, "").trim();
|
|
19800
|
+
return normalizedAnchor.length > 0 && normalizedFile.length > 0 && (normalizedFile.includes(normalizedAnchor) || normalizedAnchor.includes(normalizedFile));
|
|
19801
|
+
}
|
|
19799
19802
|
function surfaceFiles(surface, changedFiles) {
|
|
19800
19803
|
return changedFiles.filter((file) => surface.patterns.some((pattern) => pattern.test(file)));
|
|
19801
19804
|
}
|
|
19805
|
+
|
|
19806
|
+
// ../project-intelligence-contracts/src/intent-ledger.ts
|
|
19807
|
+
var PROJECT_INTENT_LEDGER_VERSION = "intent-ledger-v1";
|
|
19808
|
+
var PROJECT_INTENT_LEDGER_FRESHNESS_HORIZON_DAYS = 90;
|
|
19809
|
+
var DECISION_STATUS_RULES = [
|
|
19810
|
+
{ status: "contradicted", patterns: [/contradict/i, /conflict/i] },
|
|
19811
|
+
{ status: "superseded", patterns: [/supersed/i, /replac/i] },
|
|
19812
|
+
{ status: "stale", patterns: [/stale/i, /expired/i, /deprecated/i] },
|
|
19813
|
+
{ status: "approved", patterns: [/approved/i, /accepted/i, /active/i, /confirmed/i] }
|
|
19814
|
+
];
|
|
19815
|
+
var INTENT_PHRASE_SPLIT_PATTERN = /[;\n]/g;
|
|
19816
|
+
var INTENT_MARKDOWN_LIST_MARKER_PATTERN = /^[-*]\s*/;
|
|
19817
|
+
var INTENT_NUMBERED_LIST_MARKER_PATTERN = /^\d+\.\s*/;
|
|
19818
|
+
var INTENT_TEXT_EXTRACTORS = {
|
|
19819
|
+
antiGoals: [/\b(?:avoid|forbid|prevent|do not|don't)\b[^.;\n]*/gi],
|
|
19820
|
+
rejectedAlternatives: [/\b(?:rejected|instead of|rather than|not use|not using)\b[^.;\n]*/gi]
|
|
19821
|
+
};
|
|
19822
|
+
function intentStatusFromDecision(decision) {
|
|
19823
|
+
const status = normalizeText(decision.status);
|
|
19824
|
+
const matchedRule = DECISION_STATUS_RULES.find(
|
|
19825
|
+
(rule) => rule.patterns.some((pattern) => pattern.test(status))
|
|
19826
|
+
);
|
|
19827
|
+
return matchedRule?.status ?? "review_pending";
|
|
19828
|
+
}
|
|
19829
|
+
function intentConfidence(score, status, reasonCodes) {
|
|
19830
|
+
return {
|
|
19831
|
+
score,
|
|
19832
|
+
band: confidenceBand(score),
|
|
19833
|
+
state: status === "approved" ? "confirmed" : status === "stale" ? "stale" : status === "contradicted" ? "conflicted" : score < 0.55 ? "weak" : "review_pending",
|
|
19834
|
+
reasonCodes
|
|
19835
|
+
};
|
|
19836
|
+
}
|
|
19837
|
+
function stalenessFromStatus(status) {
|
|
19838
|
+
if (status === "stale" || status === "superseded") {
|
|
19839
|
+
return {
|
|
19840
|
+
state: "stale",
|
|
19841
|
+
reason: status === "superseded" ? "The source decision is marked as superseded." : "The source decision is marked as stale."
|
|
19842
|
+
};
|
|
19843
|
+
}
|
|
19844
|
+
if (status === "review_pending") {
|
|
19845
|
+
return {
|
|
19846
|
+
state: "unknown",
|
|
19847
|
+
reason: "The intent has not been reviewed as current project truth."
|
|
19848
|
+
};
|
|
19849
|
+
}
|
|
19850
|
+
return { state: "fresh", horizonDays: PROJECT_INTENT_LEDGER_FRESHNESS_HORIZON_DAYS };
|
|
19851
|
+
}
|
|
19852
|
+
function intentEntryMatchesFile(entry, file) {
|
|
19853
|
+
return entry.affectedAnchors.some((anchor) => anchorMatchesFile(anchor, file));
|
|
19854
|
+
}
|
|
19855
|
+
function inferDecisionAnchors(decision, changedFiles) {
|
|
19856
|
+
const explicitAnchors = uniqueStrings8(decision.affectedAnchors ?? []);
|
|
19857
|
+
if (explicitAnchors.length > 0) return explicitAnchors;
|
|
19858
|
+
const text = normalizeText(decisionText(decision));
|
|
19859
|
+
const inferred = SENSITIVE_SURFACES.flatMap(
|
|
19860
|
+
(surface) => matchesAnyText(text, surface.patterns) ? surfaceFiles(surface, changedFiles) : []
|
|
19861
|
+
);
|
|
19862
|
+
return uniqueStrings8(inferred);
|
|
19863
|
+
}
|
|
19864
|
+
function normalizeIntentPhrase(value) {
|
|
19865
|
+
return value.replace(INTENT_MARKDOWN_LIST_MARKER_PATTERN, "").replace(INTENT_NUMBERED_LIST_MARKER_PATTERN, "").trim();
|
|
19866
|
+
}
|
|
19867
|
+
function splitIntentPhrases(value) {
|
|
19868
|
+
if (!value) return [];
|
|
19869
|
+
return uniqueStrings8(
|
|
19870
|
+
value.split(INTENT_PHRASE_SPLIT_PATTERN).map(normalizeIntentPhrase).filter(Boolean)
|
|
19871
|
+
).slice(0, 6);
|
|
19872
|
+
}
|
|
19873
|
+
function extractTextMatches(text, patterns, limit) {
|
|
19874
|
+
const matches = patterns.flatMap(
|
|
19875
|
+
(pattern) => [...text.matchAll(pattern)].map((match) => match[0].trim())
|
|
19876
|
+
);
|
|
19877
|
+
return uniqueStrings8(matches).slice(0, limit);
|
|
19878
|
+
}
|
|
19879
|
+
function extractAntiGoals(text) {
|
|
19880
|
+
return extractTextMatches(text, INTENT_TEXT_EXTRACTORS.antiGoals, 5);
|
|
19881
|
+
}
|
|
19882
|
+
function extractRejectedAlternatives(text) {
|
|
19883
|
+
return extractTextMatches(text, INTENT_TEXT_EXTRACTORS.rejectedAlternatives, 5);
|
|
19884
|
+
}
|
|
19885
|
+
function documentLooksLikeIntent(doc) {
|
|
19886
|
+
const text = normalizeText([doc.path, doc.title, doc.contentPreview, doc.kind].join(" "));
|
|
19887
|
+
return /\b(adr|decision|intent|rationale|constraint|architecture|runbook)\b/.test(text);
|
|
19888
|
+
}
|
|
19889
|
+
function buildDecisionIntentEntry(decision, changedFiles) {
|
|
19890
|
+
const status = intentStatusFromDecision(decision);
|
|
19891
|
+
const anchors = inferDecisionAnchors(decision, changedFiles);
|
|
19892
|
+
const fullText = decisionText(decision);
|
|
19893
|
+
const confidenceScore = normalizeConfidenceScore(
|
|
19894
|
+
decision.confidenceScore,
|
|
19895
|
+
status === "approved" ? 0.86 : 0.66
|
|
19896
|
+
);
|
|
19897
|
+
const reasonCodes = uniqueStrings8([
|
|
19898
|
+
"decision_intent",
|
|
19899
|
+
status === "approved" ? "reviewed_source" : "review_needed",
|
|
19900
|
+
anchors.length > 0 ? "affected_anchors_present" : "affected_anchors_missing"
|
|
19901
|
+
]);
|
|
19902
|
+
return {
|
|
19903
|
+
id: `intent:${decision.id}`,
|
|
19904
|
+
title: decision.title,
|
|
19905
|
+
status,
|
|
19906
|
+
source: "decision",
|
|
19907
|
+
sourceDecisionId: decision.id,
|
|
19908
|
+
sourceDocumentPath: null,
|
|
19909
|
+
intent: {
|
|
19910
|
+
goal: decision.rationale || decision.decision || decision.title,
|
|
19911
|
+
constraints: uniqueStrings8([
|
|
19912
|
+
...decision.constraints ?? [],
|
|
19913
|
+
...splitIntentPhrases(decision.scope)
|
|
19914
|
+
]).slice(0, 8),
|
|
19915
|
+
antiGoals: extractAntiGoals(fullText),
|
|
19916
|
+
rejectedAlternatives: extractRejectedAlternatives(fullText)
|
|
19917
|
+
},
|
|
19918
|
+
affectedAnchors: anchors,
|
|
19919
|
+
owner: null,
|
|
19920
|
+
evidence: decision.evidence && decision.evidence.length > 0 ? decision.evidence : [
|
|
19921
|
+
{
|
|
19922
|
+
kind: "decision",
|
|
19923
|
+
label: `${decision.id}: ${decision.title}`,
|
|
19924
|
+
sourceRef: decision.id,
|
|
19925
|
+
strength: confidenceScore
|
|
19926
|
+
}
|
|
19927
|
+
],
|
|
19928
|
+
confidence: intentConfidence(confidenceScore, status, reasonCodes),
|
|
19929
|
+
staleness: stalenessFromStatus(status),
|
|
19930
|
+
reasonCodes,
|
|
19931
|
+
recommendedActions: status === "approved" ? ["Keep this intent linked when changing its affected anchors."] : ["Review this intent candidate before treating it as governing project truth."],
|
|
19932
|
+
caveats: [
|
|
19933
|
+
"Intent Ledger V1 preserves source-backed intent; it does not infer unstated architecture rules."
|
|
19934
|
+
]
|
|
19935
|
+
};
|
|
19936
|
+
}
|
|
19937
|
+
function buildDocumentIntentEntry(doc) {
|
|
19938
|
+
const preview4 = doc.contentPreview || doc.title || doc.path;
|
|
19939
|
+
const reasonCodes = ["document_intent_candidate", "review_needed"];
|
|
19940
|
+
const confidenceScore = doc.kind && /adr|decision/i.test(doc.kind) ? 0.66 : 0.58;
|
|
19941
|
+
return {
|
|
19942
|
+
id: `intent:doc:${doc.path}`,
|
|
19943
|
+
title: doc.title || doc.path,
|
|
19944
|
+
status: "review_pending",
|
|
19945
|
+
source: "document",
|
|
19946
|
+
sourceDecisionId: null,
|
|
19947
|
+
sourceDocumentPath: doc.path,
|
|
19948
|
+
intent: {
|
|
19949
|
+
goal: preview4,
|
|
19950
|
+
constraints: splitIntentPhrases(doc.contentPreview).slice(0, 4),
|
|
19951
|
+
antiGoals: extractAntiGoals(preview4),
|
|
19952
|
+
rejectedAlternatives: extractRejectedAlternatives(preview4)
|
|
19953
|
+
},
|
|
19954
|
+
affectedAnchors: [doc.path],
|
|
19955
|
+
owner: null,
|
|
19956
|
+
evidence: [
|
|
19957
|
+
{
|
|
19958
|
+
kind: "document",
|
|
19959
|
+
label: doc.title ? `${doc.path}: ${doc.title}` : doc.path,
|
|
19960
|
+
sourceRef: doc.sourceRef ?? doc.path,
|
|
19961
|
+
strength: confidenceScore
|
|
19962
|
+
}
|
|
19963
|
+
],
|
|
19964
|
+
confidence: intentConfidence(confidenceScore, "review_pending", reasonCodes),
|
|
19965
|
+
staleness: {
|
|
19966
|
+
state: "unknown",
|
|
19967
|
+
reason: "Document-derived intent candidates need review before becoming governing truth."
|
|
19968
|
+
},
|
|
19969
|
+
reasonCodes,
|
|
19970
|
+
recommendedActions: ["Promote, reject, or link this document intent candidate to a decision."],
|
|
19971
|
+
caveats: [
|
|
19972
|
+
"Document candidates are weaker than reviewed decisions until authority is confirmed."
|
|
19973
|
+
]
|
|
19974
|
+
};
|
|
19975
|
+
}
|
|
19976
|
+
function buildProjectIntentLedger(input, changedFilesOverride) {
|
|
19977
|
+
const changedFiles = uniqueStrings8(changedFilesOverride ?? input.changedFiles);
|
|
19978
|
+
const decisionEntries = (input.decisions ?? []).map(
|
|
19979
|
+
(decision) => buildDecisionIntentEntry(decision, changedFiles)
|
|
19980
|
+
);
|
|
19981
|
+
const decisionDocumentPaths = new Set(
|
|
19982
|
+
decisionEntries.flatMap(
|
|
19983
|
+
(entry) => entry.evidence.map((evidence) => evidence.sourceRef).filter(Boolean)
|
|
19984
|
+
)
|
|
19985
|
+
);
|
|
19986
|
+
const documentEntries = (input.documents ?? []).filter(documentLooksLikeIntent).filter((doc) => !decisionDocumentPaths.has(doc.path)).map(buildDocumentIntentEntry);
|
|
19987
|
+
const entries = [...decisionEntries, ...documentEntries];
|
|
19988
|
+
const linkedIntentIds = new Set(
|
|
19989
|
+
entries.filter((entry) => changedFiles.some((file) => intentEntryMatchesFile(entry, file))).map((entry) => entry.id)
|
|
19990
|
+
);
|
|
19991
|
+
const missingAnchors = changedFiles.filter(
|
|
19992
|
+
(file) => !entries.some((entry) => intentEntryMatchesFile(entry, file))
|
|
19993
|
+
);
|
|
19994
|
+
const coverage = changedFiles.length === 0 || entries.length > 0 && missingAnchors.length === 0 ? "linked" : entries.length === 0 ? "missing" : "partial";
|
|
19995
|
+
const reasonCodes = uniqueStrings8([
|
|
19996
|
+
coverage === "linked" ? "intent_coverage_linked" : void 0,
|
|
19997
|
+
coverage === "partial" ? "intent_coverage_partial" : void 0,
|
|
19998
|
+
coverage === "missing" ? "intent_coverage_missing" : void 0,
|
|
19999
|
+
entries.some((entry) => entry.status === "review_pending") ? "intent_review_pending" : void 0,
|
|
20000
|
+
entries.some((entry) => entry.staleness.state !== "fresh") ? "intent_freshness_unknown" : void 0
|
|
20001
|
+
]);
|
|
20002
|
+
return {
|
|
20003
|
+
version: PROJECT_INTENT_LEDGER_VERSION,
|
|
20004
|
+
coverage,
|
|
20005
|
+
totalIntentCount: entries.length,
|
|
20006
|
+
linkedIntentCount: linkedIntentIds.size,
|
|
20007
|
+
missingAnchorCount: missingAnchors.length,
|
|
20008
|
+
entries: entries.slice(0, 20),
|
|
20009
|
+
missingAnchors: missingAnchors.slice(0, 20),
|
|
20010
|
+
evidence: entries.flatMap((entry) => entry.evidence).slice(0, 10),
|
|
20011
|
+
reasonCodes,
|
|
20012
|
+
caveats: [
|
|
20013
|
+
"Intent Ledger V1 is a deterministic projection from supplied decisions and documents.",
|
|
20014
|
+
coverage === "missing" ? "Missing intent means no linked source was supplied for this scope, not that no governing intent exists." : void 0
|
|
20015
|
+
].filter((item) => Boolean(item))
|
|
20016
|
+
};
|
|
20017
|
+
}
|
|
20018
|
+
|
|
20019
|
+
// ../project-intelligence-contracts/src/unknown-registry.ts
|
|
20020
|
+
var PROJECT_UNKNOWN_REGISTRY_VERSION = "unknown-registry-v1";
|
|
20021
|
+
function unknownId(category, key, scope) {
|
|
20022
|
+
return makeScopedId("unknown", category, key, scope);
|
|
20023
|
+
}
|
|
20024
|
+
function unknownStatusFromSeverity(unknowns) {
|
|
20025
|
+
if (unknowns.some(
|
|
20026
|
+
(unknown) => unknown.severity === "blocking" || unknown.severity === "review_required"
|
|
20027
|
+
)) {
|
|
20028
|
+
return "risk";
|
|
20029
|
+
}
|
|
20030
|
+
if (unknowns.some((unknown) => unknown.severity === "watch")) return "watch";
|
|
20031
|
+
return "clear";
|
|
20032
|
+
}
|
|
20033
|
+
function unknownConfidence(score, state, reasonCodes) {
|
|
20034
|
+
return {
|
|
20035
|
+
score,
|
|
20036
|
+
band: confidenceBand(score),
|
|
20037
|
+
state,
|
|
20038
|
+
reasonCodes
|
|
20039
|
+
};
|
|
20040
|
+
}
|
|
20041
|
+
function makeUnknown(input) {
|
|
20042
|
+
return {
|
|
20043
|
+
id: unknownId(input.category, input.title, input.scope),
|
|
20044
|
+
category: input.category,
|
|
20045
|
+
severity: input.severity,
|
|
20046
|
+
title: input.title,
|
|
20047
|
+
summary: input.summary,
|
|
20048
|
+
scope: input.scope.slice(0, 12),
|
|
20049
|
+
ownerCandidate: input.ownerCandidate ?? null,
|
|
20050
|
+
confidence: unknownConfidence(
|
|
20051
|
+
input.confidenceScore,
|
|
20052
|
+
input.confidenceState ?? "review_pending",
|
|
20053
|
+
input.reasonCodes
|
|
20054
|
+
),
|
|
20055
|
+
linkedFindingIds: input.linkedFindingIds ?? [],
|
|
20056
|
+
linkedIntentIds: input.linkedIntentIds ?? [],
|
|
20057
|
+
evidence: (input.evidence ?? []).slice(0, 8),
|
|
20058
|
+
reasonCodes: input.reasonCodes,
|
|
20059
|
+
resolutionActions: input.resolutionActions,
|
|
20060
|
+
closureEvidenceRequired: input.closureEvidenceRequired,
|
|
20061
|
+
caveats: input.caveats ?? []
|
|
20062
|
+
};
|
|
20063
|
+
}
|
|
20064
|
+
function buildProjectUnknownRegistry(input) {
|
|
20065
|
+
const unknowns = [];
|
|
20066
|
+
if (input.intentLedger.missingAnchors.length > 0) {
|
|
20067
|
+
unknowns.push(
|
|
20068
|
+
makeUnknown({
|
|
20069
|
+
category: "missing_intent",
|
|
20070
|
+
severity: input.findings.some((finding) => finding.severity === "review_required") ? "review_required" : "watch",
|
|
20071
|
+
title: "Governing intent is not linked for part of this scope",
|
|
20072
|
+
summary: "At least one changed anchor has no linked reviewed decision or document intent. This is an unknown, not proof that no constraint exists.",
|
|
20073
|
+
scope: input.intentLedger.missingAnchors,
|
|
20074
|
+
confidenceScore: 0.86,
|
|
20075
|
+
linkedFindingIds: input.findings.filter((finding) => finding.reasonCodes.includes("missing_intent")).map((finding) => finding.id),
|
|
20076
|
+
evidence: input.intentLedger.evidence,
|
|
20077
|
+
reasonCodes: ["missing_intent", "unknown_governing_decision"],
|
|
20078
|
+
resolutionActions: [
|
|
20079
|
+
"Link an existing decision, approve a review-pending intent, or create a new decision candidate."
|
|
20080
|
+
],
|
|
20081
|
+
closureEvidenceRequired: [
|
|
20082
|
+
"Reviewed decision, ADR, issue, PR Answer Pack, or final workflow commit linked to the changed anchor."
|
|
20083
|
+
],
|
|
20084
|
+
caveats: [
|
|
20085
|
+
"Retrieval/index freshness can make linked intent appear missing until context is refreshed."
|
|
20086
|
+
]
|
|
20087
|
+
})
|
|
20088
|
+
);
|
|
20089
|
+
}
|
|
20090
|
+
for (const entry of input.intentLedger.entries) {
|
|
20091
|
+
if (entry.status === "review_pending") {
|
|
20092
|
+
unknowns.push(
|
|
20093
|
+
makeUnknown({
|
|
20094
|
+
category: "intent_review_pending",
|
|
20095
|
+
severity: "watch",
|
|
20096
|
+
title: `Intent needs review: ${entry.title}`,
|
|
20097
|
+
summary: "A source-backed intent candidate exists, but it is not reviewed enough to act as governing project truth.",
|
|
20098
|
+
scope: entry.affectedAnchors,
|
|
20099
|
+
confidenceScore: entry.confidence.score,
|
|
20100
|
+
linkedIntentIds: [entry.id],
|
|
20101
|
+
evidence: entry.evidence,
|
|
20102
|
+
reasonCodes: ["intent_review_pending"],
|
|
20103
|
+
resolutionActions: ["Approve, reject, supersede, or link this intent candidate."],
|
|
20104
|
+
closureEvidenceRequired: ["Review receipt or approved ProjectDecision authority state."],
|
|
20105
|
+
caveats: entry.caveats
|
|
20106
|
+
})
|
|
20107
|
+
);
|
|
20108
|
+
}
|
|
20109
|
+
if (entry.status === "stale" || entry.status === "superseded") {
|
|
20110
|
+
unknowns.push(
|
|
20111
|
+
makeUnknown({
|
|
20112
|
+
category: "stale_intent",
|
|
20113
|
+
severity: "review_required",
|
|
20114
|
+
title: `Intent may be stale: ${entry.title}`,
|
|
20115
|
+
summary: "A linked intent is stale or superseded; current implementation may no longer match the original rationale.",
|
|
20116
|
+
scope: entry.affectedAnchors,
|
|
20117
|
+
confidenceScore: 0.78,
|
|
20118
|
+
confidenceState: "stale",
|
|
20119
|
+
linkedIntentIds: [entry.id],
|
|
20120
|
+
evidence: entry.evidence,
|
|
20121
|
+
reasonCodes: ["stale_intent", entry.status],
|
|
20122
|
+
resolutionActions: [
|
|
20123
|
+
"Refresh, supersede, or archive the stale intent before relying on it."
|
|
20124
|
+
],
|
|
20125
|
+
closureEvidenceRequired: ["Updated decision or explicit supersession evidence."],
|
|
20126
|
+
caveats: entry.caveats
|
|
20127
|
+
})
|
|
20128
|
+
);
|
|
20129
|
+
}
|
|
20130
|
+
}
|
|
20131
|
+
for (const finding of input.findings) {
|
|
20132
|
+
if (finding.reasonCodes.includes("verification_missing")) {
|
|
20133
|
+
unknowns.push(
|
|
20134
|
+
makeUnknown({
|
|
20135
|
+
category: "missing_verification",
|
|
20136
|
+
severity: finding.severity,
|
|
20137
|
+
title: `Verification gap: ${finding.title}`,
|
|
20138
|
+
summary: "The change touches a sensitive surface but no matching verification evidence was supplied.",
|
|
20139
|
+
scope: finding.changedFiles,
|
|
20140
|
+
confidenceScore: 0.82,
|
|
20141
|
+
linkedFindingIds: [finding.id],
|
|
20142
|
+
linkedIntentIds: finding.decisionIds.map((id) => `intent:${id}`),
|
|
20143
|
+
evidence: finding.evidence,
|
|
20144
|
+
reasonCodes: ["missing_verification", ...finding.reasonCodes],
|
|
20145
|
+
resolutionActions: finding.recommendedActions,
|
|
20146
|
+
closureEvidenceRequired: [
|
|
20147
|
+
"Focused test, smoke check, review proof, or verification checklist item linked to the changed behavior."
|
|
20148
|
+
],
|
|
20149
|
+
caveats: finding.caveats
|
|
20150
|
+
})
|
|
20151
|
+
);
|
|
20152
|
+
}
|
|
20153
|
+
if (finding.type === "architecture_drift") {
|
|
20154
|
+
unknowns.push(
|
|
20155
|
+
makeUnknown({
|
|
20156
|
+
category: "architecture_drift",
|
|
20157
|
+
severity: finding.severity,
|
|
20158
|
+
title: finding.title,
|
|
20159
|
+
summary: "A boundary-spanning change needs architecture-intent confirmation before treating the current dependency shape as intended.",
|
|
20160
|
+
scope: finding.changedFiles,
|
|
20161
|
+
confidenceScore: 0.72,
|
|
20162
|
+
linkedFindingIds: [finding.id],
|
|
20163
|
+
evidence: finding.evidence,
|
|
20164
|
+
reasonCodes: ["architecture_drift_unknown", ...finding.reasonCodes],
|
|
20165
|
+
resolutionActions: finding.recommendedActions,
|
|
20166
|
+
closureEvidenceRequired: [
|
|
20167
|
+
"Architecture rule, reviewed decision, or code graph evidence proving the boundary is intentional."
|
|
20168
|
+
],
|
|
20169
|
+
caveats: finding.caveats
|
|
20170
|
+
})
|
|
20171
|
+
);
|
|
20172
|
+
}
|
|
20173
|
+
if (finding.reasonCodes.includes("risk_term_in_diff") || finding.reasonCodes.includes("no_linked_decision") && finding.severity === "review_required") {
|
|
20174
|
+
unknowns.push(
|
|
20175
|
+
makeUnknown({
|
|
20176
|
+
category: "heuristic_calibration",
|
|
20177
|
+
severity: "watch",
|
|
20178
|
+
title: `Heuristic signal needs calibration: ${finding.title}`,
|
|
20179
|
+
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.",
|
|
20180
|
+
scope: finding.changedFiles,
|
|
20181
|
+
confidenceScore: 0.68,
|
|
20182
|
+
confidenceState: "weak",
|
|
20183
|
+
linkedFindingIds: [finding.id],
|
|
20184
|
+
evidence: finding.evidence,
|
|
20185
|
+
reasonCodes: ["heuristic_calibration_needed", ...finding.reasonCodes],
|
|
20186
|
+
resolutionActions: [
|
|
20187
|
+
"Confirm the finding against structured intent, tests, or reviewed project-specific rules before broad CI gating."
|
|
20188
|
+
],
|
|
20189
|
+
closureEvidenceRequired: [
|
|
20190
|
+
"Reviewed calibration decision or false-positive/true-positive outcome history for this surface."
|
|
20191
|
+
],
|
|
20192
|
+
caveats: [
|
|
20193
|
+
"--enforce should remain opt-in for this finding family until calibration evidence exists."
|
|
20194
|
+
]
|
|
20195
|
+
})
|
|
20196
|
+
);
|
|
20197
|
+
}
|
|
20198
|
+
}
|
|
20199
|
+
if (input.dirtyFiles.length > 0) {
|
|
20200
|
+
unknowns.push(
|
|
20201
|
+
makeUnknown({
|
|
20202
|
+
category: "dirty_local_evidence",
|
|
20203
|
+
severity: "watch",
|
|
20204
|
+
title: "Local dirty evidence is not hosted truth",
|
|
20205
|
+
summary: "The local scan included dirty files that hosted PR checks and code graph indexes cannot see until committed.",
|
|
20206
|
+
scope: input.dirtyFiles,
|
|
20207
|
+
confidenceScore: 0.9,
|
|
20208
|
+
linkedFindingIds: input.findings.filter((finding) => finding.reasonCodes.includes("dirty_working_tree")).map((finding) => finding.id),
|
|
20209
|
+
evidence: input.dirtyFiles.map((file) => ({
|
|
20210
|
+
kind: "repository",
|
|
20211
|
+
label: file,
|
|
20212
|
+
sourceRef: file,
|
|
20213
|
+
strength: 0.5
|
|
20214
|
+
})),
|
|
20215
|
+
reasonCodes: ["dirty_local_evidence", "hosted_context_gap"],
|
|
20216
|
+
resolutionActions: [
|
|
20217
|
+
"Commit, stash, or exclude dirty files before relying on hosted checks."
|
|
20218
|
+
],
|
|
20219
|
+
closureEvidenceRequired: ["Clean git status or explicit review of dirty files."],
|
|
20220
|
+
caveats: ["Dirty-file unknowns are local-only and should not be treated as PR evidence."]
|
|
20221
|
+
})
|
|
20222
|
+
);
|
|
20223
|
+
}
|
|
20224
|
+
const deduped = Array.from(
|
|
20225
|
+
new Map(unknowns.map((unknown) => [unknown.id, unknown])).values()
|
|
20226
|
+
).slice(0, 24);
|
|
20227
|
+
const status = unknownStatusFromSeverity(deduped);
|
|
20228
|
+
return {
|
|
20229
|
+
version: PROJECT_UNKNOWN_REGISTRY_VERSION,
|
|
20230
|
+
status,
|
|
20231
|
+
unknownCount: deduped.length,
|
|
20232
|
+
riskCount: deduped.filter(
|
|
20233
|
+
(unknown) => unknown.severity === "review_required" || unknown.severity === "blocking"
|
|
20234
|
+
).length,
|
|
20235
|
+
watchCount: deduped.filter((unknown) => unknown.severity === "watch").length,
|
|
20236
|
+
categories: uniqueStrings8(
|
|
20237
|
+
deduped.map((unknown) => unknown.category)
|
|
20238
|
+
),
|
|
20239
|
+
unknowns: deduped,
|
|
20240
|
+
evidence: deduped.flatMap((unknown) => unknown.evidence).slice(0, 12),
|
|
20241
|
+
reasonCodes: uniqueStrings8(deduped.flatMap((unknown) => unknown.reasonCodes)).slice(0, 24),
|
|
20242
|
+
caveats: [
|
|
20243
|
+
"Unknown Registry V1 records evidence-backed gaps; closing an unknown requires source-backed evidence.",
|
|
20244
|
+
deduped.some((unknown) => unknown.category === "heuristic_calibration") ? "Heuristic calibration unknowns prevent regex/path findings from masquerading as fully calibrated truth." : void 0
|
|
20245
|
+
].filter((item) => Boolean(item))
|
|
20246
|
+
};
|
|
20247
|
+
}
|
|
20248
|
+
|
|
20249
|
+
// ../project-intelligence-contracts/src/reality-check.ts
|
|
20250
|
+
var PROJECT_REALITY_CHECK_VERSION = "project-reality-check-v0";
|
|
20251
|
+
function findingSeverityScore(severity) {
|
|
20252
|
+
if (severity === "blocking") return 0;
|
|
20253
|
+
if (severity === "review_required") return 30;
|
|
20254
|
+
if (severity === "watch") return 70;
|
|
20255
|
+
return 90;
|
|
20256
|
+
}
|
|
20257
|
+
function resultStatusFromFindings(findings) {
|
|
20258
|
+
if (findings.some((finding) => finding.severity === "blocking")) return "blocking";
|
|
20259
|
+
if (findings.some((finding) => finding.severity === "review_required")) return "review_required";
|
|
20260
|
+
if (findings.some((finding) => finding.severity === "watch")) return "advisory";
|
|
20261
|
+
return "pass";
|
|
20262
|
+
}
|
|
20263
|
+
function makeFindingId(type, key, files) {
|
|
20264
|
+
return makeScopedId("reality", type, key, files);
|
|
20265
|
+
}
|
|
19802
20266
|
function testFiles(changedFiles) {
|
|
19803
20267
|
return changedFiles.filter(
|
|
19804
20268
|
(file) => /(^|\/)(__tests__|tests?|e2e)(\/|$)|(\.test|\.spec)\.[jt]sx?$/i.test(file)
|
|
@@ -19836,6 +20300,7 @@ function buildProjectRealityCheck(input) {
|
|
|
19836
20300
|
const findings = [];
|
|
19837
20301
|
const decisions = input.decisions ?? [];
|
|
19838
20302
|
const contextEvidence2 = [...docEvidence(input), ...symbolEvidence(input)];
|
|
20303
|
+
const intentLedger = buildProjectIntentLedger(input, changedFiles);
|
|
19839
20304
|
for (const surface of SENSITIVE_SURFACES) {
|
|
19840
20305
|
const files = surfaceFiles(surface, changedFiles);
|
|
19841
20306
|
if (files.length === 0) continue;
|
|
@@ -19896,7 +20361,7 @@ function buildProjectRealityCheck(input) {
|
|
|
19896
20361
|
]
|
|
19897
20362
|
});
|
|
19898
20363
|
}
|
|
19899
|
-
if (changedFiles.length > 0 &&
|
|
20364
|
+
if (changedFiles.length > 0 && intentLedger.linkedIntentCount === 0 && (input.source === "pull_request" || findings.length > 0)) {
|
|
19900
20365
|
findings.push({
|
|
19901
20366
|
id: makeFindingId("decision_vs_code", "missing_intent", changedFiles),
|
|
19902
20367
|
type: "decision_vs_code",
|
|
@@ -19961,6 +20426,13 @@ function buildProjectRealityCheck(input) {
|
|
|
19961
20426
|
0,
|
|
19962
20427
|
20
|
|
19963
20428
|
);
|
|
20429
|
+
const unknownRegistry = buildProjectUnknownRegistry({
|
|
20430
|
+
realityCheck: input,
|
|
20431
|
+
changedFiles,
|
|
20432
|
+
dirtyFiles,
|
|
20433
|
+
findings,
|
|
20434
|
+
intentLedger
|
|
20435
|
+
});
|
|
19964
20436
|
const caveats = uniqueStrings8([
|
|
19965
20437
|
"Project Reality Check V0 is advisory and evidence-backed; it is not deterministic repository simulation.",
|
|
19966
20438
|
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 +20447,15 @@ function buildProjectRealityCheck(input) {
|
|
|
19975
20447
|
changedFileCount: changedFiles.length,
|
|
19976
20448
|
findingCount: findings.length,
|
|
19977
20449
|
findings,
|
|
20450
|
+
intentLedger,
|
|
20451
|
+
unknownRegistry,
|
|
19978
20452
|
requiredActions,
|
|
19979
20453
|
watchItems,
|
|
19980
|
-
reasonCodes
|
|
20454
|
+
reasonCodes: uniqueStrings8([
|
|
20455
|
+
...reasonCodes,
|
|
20456
|
+
...intentLedger.reasonCodes,
|
|
20457
|
+
...unknownRegistry.reasonCodes
|
|
20458
|
+
]).slice(0, 30),
|
|
19981
20459
|
caveats
|
|
19982
20460
|
};
|
|
19983
20461
|
}
|
|
@@ -19987,6 +20465,45 @@ function renderProjectRealityCheckMarkdown(result) {
|
|
|
19987
20465
|
`- Scope: ${result.changedFileCount} changed file(s), ${result.findingCount} finding(s).`,
|
|
19988
20466
|
`- Summary: ${result.summary}`
|
|
19989
20467
|
];
|
|
20468
|
+
lines.push(
|
|
20469
|
+
"",
|
|
20470
|
+
"Intent Ledger:",
|
|
20471
|
+
`- Coverage: ${result.intentLedger.coverage}; ${result.intentLedger.linkedIntentCount}/${result.intentLedger.totalIntentCount} intent(s) linked to this scope.`
|
|
20472
|
+
);
|
|
20473
|
+
for (const entry of result.intentLedger.entries.slice(0, 5)) {
|
|
20474
|
+
lines.push(
|
|
20475
|
+
`- ${entry.status.toUpperCase()} ${entry.id}: ${entry.title}`,
|
|
20476
|
+
` Intent: ${entry.intent.goal}`,
|
|
20477
|
+
` Confidence: ${entry.confidence.band} (${Math.round(entry.confidence.score * 100)}%).`
|
|
20478
|
+
);
|
|
20479
|
+
if (entry.affectedAnchors.length > 0) {
|
|
20480
|
+
lines.push(` Anchors: ${entry.affectedAnchors.slice(0, 5).join(", ")}`);
|
|
20481
|
+
}
|
|
20482
|
+
}
|
|
20483
|
+
if (result.intentLedger.missingAnchors.length > 0) {
|
|
20484
|
+
lines.push(
|
|
20485
|
+
`- Missing linked intent for: ${result.intentLedger.missingAnchors.slice(0, 8).map((file) => `\`${file}\``).join(", ")}`
|
|
20486
|
+
);
|
|
20487
|
+
}
|
|
20488
|
+
lines.push(
|
|
20489
|
+
"",
|
|
20490
|
+
"Unknown Registry:",
|
|
20491
|
+
`- Status: ${result.unknownRegistry.status}; ${result.unknownRegistry.unknownCount} unknown(s), ${result.unknownRegistry.riskCount} risk item(s).`
|
|
20492
|
+
);
|
|
20493
|
+
for (const unknown of result.unknownRegistry.unknowns.slice(0, 6)) {
|
|
20494
|
+
lines.push(
|
|
20495
|
+
`- ${unknown.severity.toUpperCase()} ${unknown.category}: ${unknown.title}`,
|
|
20496
|
+
` ${unknown.summary}`
|
|
20497
|
+
);
|
|
20498
|
+
if (unknown.scope.length > 0) {
|
|
20499
|
+
lines.push(
|
|
20500
|
+
` Scope: ${unknown.scope.slice(0, 5).map((item) => `\`${item}\``).join(", ")}`
|
|
20501
|
+
);
|
|
20502
|
+
}
|
|
20503
|
+
if (unknown.resolutionActions.length > 0) {
|
|
20504
|
+
lines.push(` Resolve: ${unknown.resolutionActions[0]}`);
|
|
20505
|
+
}
|
|
20506
|
+
}
|
|
19990
20507
|
if (result.findings.length > 0) {
|
|
19991
20508
|
lines.push("", "Findings:");
|
|
19992
20509
|
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
|