@amityco/social-plus-vise 0.14.8 → 0.14.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/CHANGELOG.md +6 -0
- package/dist/outcomes.js +5 -4
- package/dist/productExpectations.js +56 -0
- package/dist/tools/compliance.js +68 -23
- package/dist/tools/debug.js +4 -2
- package/dist/tools/project.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@amityco/social-plus-vise` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.14.9 — 2026-06-05
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **Product expectation bindings:** product-flow checks now surface platform-agnostic expectation IDs such as `chat.unread-visible`, `chat.message-order-explicit`, `feed.rich-post-composer-scope`, and `profile.social-counts` while retaining the concrete platform validator as `sensorId`/`validator` evidence.
|
|
11
|
+
- **Backwards-compatible compliance contracts:** existing Android rule contracts continue to sync, attest, and explain through their legacy contract IDs, while `vise plan` and `vise check` use the shared product expectation IDs as the public-facing rule IDs.
|
|
12
|
+
|
|
7
13
|
## 0.14.8 — 2026-06-05
|
|
8
14
|
|
|
9
15
|
### Added
|
package/dist/outcomes.js
CHANGED
|
@@ -657,9 +657,9 @@ const addFeed = {
|
|
|
657
657
|
`${platform}.feed.post-datatype-handled`,
|
|
658
658
|
...(platform === "android"
|
|
659
659
|
? [
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
660
|
+
"feed.rich-post-composer-scope",
|
|
661
|
+
"comments.thread-read-write",
|
|
662
|
+
"profile.social-counts",
|
|
663
663
|
]
|
|
664
664
|
: []),
|
|
665
665
|
],
|
|
@@ -1027,7 +1027,7 @@ const addChat = {
|
|
|
1027
1027
|
`${platform}.chat.message-observer-cleanup`,
|
|
1028
1028
|
`${platform}.chat.send-error-handling`,
|
|
1029
1029
|
`${platform}.chat.moderation-affordance-present`,
|
|
1030
|
-
...(platform === "android" ? [
|
|
1030
|
+
...(platform === "android" ? ["chat.unread-visible", "chat.message-order-explicit"] : []),
|
|
1031
1031
|
],
|
|
1032
1032
|
stopConditions: (ctx) => filterStops(ctx.answers, [
|
|
1033
1033
|
{ id: "chat_shape", text: "The chat shape is unknown; cannot implement without knowing 1:1, group, or community channel." },
|
|
@@ -1271,6 +1271,7 @@ const addFollow = {
|
|
|
1271
1271
|
validation: () => [
|
|
1272
1272
|
"follower/following lists use a Live Collection (not a one-shot query)",
|
|
1273
1273
|
"follow model handled (automatic vs follow-request approval)",
|
|
1274
|
+
"profile.social-counts",
|
|
1274
1275
|
"no invented userId",
|
|
1275
1276
|
"relationship observer cleaned up on lifecycle end",
|
|
1276
1277
|
"validate_setup",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const PRODUCT_EXPECTATION_BINDINGS = [
|
|
2
|
+
{
|
|
3
|
+
expectationId: "feed.rich-post-composer-scope",
|
|
4
|
+
sensorId: "android.feed.rich-post-composer-surfaced",
|
|
5
|
+
platform: "android",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
expectationId: "comments.thread-read-write",
|
|
9
|
+
sensorId: "android.comments.thread-ui-states-present",
|
|
10
|
+
platform: "android",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
expectationId: "chat.unread-visible",
|
|
14
|
+
sensorId: "android.chat.unread-visible",
|
|
15
|
+
platform: "android",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
expectationId: "chat.message-order-explicit",
|
|
19
|
+
sensorId: "android.chat.sort-explicit",
|
|
20
|
+
platform: "android",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
expectationId: "profile.social-counts",
|
|
24
|
+
sensorId: "android.profile.social-counts-from-sdk",
|
|
25
|
+
platform: "android",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const bindingsBySensorId = new Map(PRODUCT_EXPECTATION_BINDINGS.map((binding) => [binding.sensorId, binding]));
|
|
29
|
+
export function productExpectationBindingForSensor(sensorId) {
|
|
30
|
+
return bindingsBySensorId.get(sensorId);
|
|
31
|
+
}
|
|
32
|
+
export function productFindingIdentity(sensorId) {
|
|
33
|
+
const binding = productExpectationBindingForSensor(sensorId);
|
|
34
|
+
if (!binding) {
|
|
35
|
+
return { ruleId: sensorId };
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
ruleId: binding.expectationId,
|
|
39
|
+
sensorId: binding.sensorId,
|
|
40
|
+
validator: {
|
|
41
|
+
platform: binding.platform,
|
|
42
|
+
sensorId: binding.sensorId,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function publicProductRuleId(ruleId) {
|
|
47
|
+
return productExpectationBindingForSensor(ruleId)?.expectationId ?? ruleId;
|
|
48
|
+
}
|
|
49
|
+
export function findingMatchesId(finding, id) {
|
|
50
|
+
return finding.ruleId === id || finding.sensorId === id;
|
|
51
|
+
}
|
|
52
|
+
export function contractRuleCandidatesForPublicId(ruleId) {
|
|
53
|
+
return PRODUCT_EXPECTATION_BINDINGS
|
|
54
|
+
.filter((binding) => binding.expectationId === ruleId)
|
|
55
|
+
.map((binding) => binding.sensorId);
|
|
56
|
+
}
|
package/dist/tools/compliance.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { assessProjectCompleteness, assessProjectSelectedOptionalCapabilities, availableOptionalCapabilityIds, optionalCapabilityChecklist, platformCapabilityAvailability, selectedOptionalCapabilityIds, } from "../capabilities.js";
|
|
6
6
|
import { getOutcomeDefinition, hasAnswer, planContextFor, resolveOutcome, } from "../outcomes.js";
|
|
7
|
+
import { contractRuleCandidatesForPublicId, productExpectationBindingForSensor, publicProductRuleId, } from "../productExpectations.js";
|
|
7
8
|
import { objectInput, optionalBooleanField, optionalStringField, stringField, textResult } from "../types.js";
|
|
8
9
|
import { packageVersion } from "../version.js";
|
|
9
10
|
import { DESIGN_CONTRACT_CONFIRMATION_ANSWER_ID, buildDesignBrief, designContractConfirmationFromAnswers, designPreviewPath, readDesignContract, } from "./design.js";
|
|
@@ -497,13 +498,19 @@ export async function checkCompliance(repoPath) {
|
|
|
497
498
|
const platforms = platformsToValidate.length > 0 ? platformsToValidate : ["unknown"];
|
|
498
499
|
const allFindings = await Promise.all(platforms.map((p) => validateSetup(inspection.effectiveRoot, p)));
|
|
499
500
|
const findings = allFindings.flat();
|
|
500
|
-
const findingsById = new Map(
|
|
501
|
+
const findingsById = new Map();
|
|
502
|
+
for (const finding of findings) {
|
|
503
|
+
findingsById.set(finding.ruleId, finding);
|
|
504
|
+
if (finding.sensorId) {
|
|
505
|
+
findingsById.set(finding.sensorId, finding);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
501
508
|
const attestations = await readAttestations(repoRoot);
|
|
502
509
|
const results = [];
|
|
503
510
|
for (const ref of compliance.rules) {
|
|
504
511
|
const rule = rules.get(ref.rule_id);
|
|
505
512
|
if (!rule) {
|
|
506
|
-
results.push({
|
|
513
|
+
results.push({ ...checkRuleIdentity(ref.rule_id), title: ref.rule_id, severity: ref.severity, status: "stale", reason: "Rule is missing from installed Vise." });
|
|
507
514
|
continue;
|
|
508
515
|
}
|
|
509
516
|
// Blockers run first. If any external prerequisite is missing, the rule is
|
|
@@ -514,7 +521,7 @@ export async function checkCompliance(repoPath) {
|
|
|
514
521
|
if (blockersFired.length > 0) {
|
|
515
522
|
const attestable = rule.enforcement.attestation.allowed;
|
|
516
523
|
results.push({
|
|
517
|
-
|
|
524
|
+
...checkRuleIdentity(rule.id),
|
|
518
525
|
title: rule.title,
|
|
519
526
|
severity: rule.severity,
|
|
520
527
|
status: "blocked",
|
|
@@ -532,7 +539,7 @@ export async function checkCompliance(repoPath) {
|
|
|
532
539
|
const finding = hasDeterministicChecks ? deterministicFinding(rule, findingsById) : undefined;
|
|
533
540
|
if (hasDeterministicChecks && !finding) {
|
|
534
541
|
results.push({
|
|
535
|
-
|
|
542
|
+
...checkRuleIdentity(rule.id),
|
|
536
543
|
title: rule.title,
|
|
537
544
|
severity: rule.severity,
|
|
538
545
|
status: "deterministic-pass",
|
|
@@ -548,7 +555,7 @@ export async function checkCompliance(repoPath) {
|
|
|
548
555
|
if (attestation.status === "deterministic-pass") {
|
|
549
556
|
const failStatus = rule.advisory ? "advisory" : rule.enforcement.attestation.allowed ? "attestation-needed" : "deterministic-fail";
|
|
550
557
|
results.push({
|
|
551
|
-
|
|
558
|
+
...checkRuleIdentity(rule.id),
|
|
552
559
|
title: rule.title,
|
|
553
560
|
severity: rule.severity,
|
|
554
561
|
status: failStatus,
|
|
@@ -574,7 +581,7 @@ export async function checkCompliance(repoPath) {
|
|
|
574
581
|
if (staleFingerprints.length > 0) {
|
|
575
582
|
const fingerprintStatus = rule.advisory ? "advisory" : rule.enforcement.attestation.allowed ? "attestation-needed" : "deterministic-fail";
|
|
576
583
|
results.push({
|
|
577
|
-
|
|
584
|
+
...checkRuleIdentity(rule.id),
|
|
578
585
|
title: rule.title,
|
|
579
586
|
severity: rule.severity,
|
|
580
587
|
status: fingerprintStatus,
|
|
@@ -592,7 +599,7 @@ export async function checkCompliance(repoPath) {
|
|
|
592
599
|
}
|
|
593
600
|
const deprecated = grandfathered && (rule.deprecated_versions ?? []).includes(attestation.rule_version);
|
|
594
601
|
results.push({
|
|
595
|
-
|
|
602
|
+
...checkRuleIdentity(rule.id),
|
|
596
603
|
title: rule.title,
|
|
597
604
|
severity: rule.severity,
|
|
598
605
|
status: "attested",
|
|
@@ -623,7 +630,7 @@ export async function checkCompliance(repoPath) {
|
|
|
623
630
|
fallbackReason = "Deterministic check failed and no valid attestation exists.";
|
|
624
631
|
}
|
|
625
632
|
results.push({
|
|
626
|
-
|
|
633
|
+
...checkRuleIdentity(rule.id),
|
|
627
634
|
title: rule.title,
|
|
628
635
|
severity: rule.severity,
|
|
629
636
|
status: baseStatus,
|
|
@@ -692,9 +699,10 @@ export async function syncCompliance(repoPath) {
|
|
|
692
699
|
const removed = [];
|
|
693
700
|
await mkdir(attestationsDir(repoRoot), { recursive: true });
|
|
694
701
|
for (const result of check.rules) {
|
|
695
|
-
const
|
|
702
|
+
const contractRuleId = result.contractRuleId ?? result.ruleId;
|
|
703
|
+
const filePath = path.join(attestationsDir(repoRoot), attestationPathFor(contractRuleId));
|
|
696
704
|
if (result.status === "deterministic-pass") {
|
|
697
|
-
const rule = (await rulesById()).get(
|
|
705
|
+
const rule = (await rulesById()).get(contractRuleId);
|
|
698
706
|
if (!rule) {
|
|
699
707
|
continue;
|
|
700
708
|
}
|
|
@@ -727,17 +735,18 @@ export async function attestRule(args) {
|
|
|
727
735
|
const repoRoot = path.resolve(args.repoPath);
|
|
728
736
|
const compliance = await readCompliance(repoRoot);
|
|
729
737
|
const rules = await rulesById();
|
|
730
|
-
const
|
|
731
|
-
|
|
738
|
+
const contractRuleId = resolveRuleIdForContract(args.ruleId, compliance, rules);
|
|
739
|
+
const rule = contractRuleId ? rules.get(contractRuleId) : undefined;
|
|
740
|
+
if (!rule || !contractRuleId) {
|
|
732
741
|
// Collect up to 8 applicable attestable rule ids from this contract for the error hint. Prefer
|
|
733
742
|
// ids that share the bad id's non-wildcard prefix so the agent can narrow down quickly.
|
|
734
743
|
const attestableIds = compliance.rules
|
|
735
744
|
.map((ref) => rules.get(ref.rule_id))
|
|
736
745
|
.filter((r) => r !== undefined && r.enforcement.attestation.allowed);
|
|
737
746
|
const prefix = args.ruleId.replace(/\.\*$|\*$/, "");
|
|
738
|
-
const prefixed = prefix !== args.ruleId ? attestableIds.filter((r) => r.id.startsWith(prefix) || r.id.includes(prefix)) : [];
|
|
747
|
+
const prefixed = prefix !== args.ruleId ? attestableIds.filter((r) => r.id.startsWith(prefix) || r.id.includes(prefix) || publicProductRuleId(r.id).startsWith(prefix)) : [];
|
|
739
748
|
const candidates = prefixed.length > 0 ? prefixed : attestableIds;
|
|
740
|
-
const hintIds = candidates.slice(0, 8).map((r) => r.id);
|
|
749
|
+
const hintIds = candidates.slice(0, 8).map((r) => publicProductRuleId(r.id));
|
|
741
750
|
const hintSuffix = hintIds.length > 0 ? ` Applicable attestable rules: ${hintIds.join(", ")}.` : " Applicable attestable rules: none.";
|
|
742
751
|
const preamble = args.ruleId.includes("*")
|
|
743
752
|
? `Wildcards are not supported — attest one rule at a time.`
|
|
@@ -757,11 +766,11 @@ export async function attestRule(args) {
|
|
|
757
766
|
await mkdir(attestationsDir(repoRoot), { recursive: true });
|
|
758
767
|
const sourceFingerprints = await collectSourceFingerprints(repoRoot, sourceRootForCompliance(repoRoot, compliance), args.evidence);
|
|
759
768
|
const attestation = buildAttestation(compliance, rule, args.signer, args.confidence, args.identity, args.rationale, args.evidence, sourceFingerprints);
|
|
760
|
-
const filePath = path.join(attestationsDir(repoRoot), attestationPathFor(
|
|
769
|
+
const filePath = path.join(attestationsDir(repoRoot), attestationPathFor(contractRuleId));
|
|
761
770
|
await writeJson(filePath, attestation);
|
|
762
771
|
return {
|
|
763
772
|
status: "attested",
|
|
764
|
-
|
|
773
|
+
...checkRuleIdentity(contractRuleId),
|
|
765
774
|
destination: filePath,
|
|
766
775
|
signer_claim: attestation.signer_claim,
|
|
767
776
|
source_fingerprints: sourceFingerprints,
|
|
@@ -769,12 +778,14 @@ export async function attestRule(args) {
|
|
|
769
778
|
}
|
|
770
779
|
export async function explainRule(ruleId) {
|
|
771
780
|
const rules = await rulesById();
|
|
772
|
-
const
|
|
773
|
-
|
|
781
|
+
const contractRuleId = resolveRuleIdForInstalledRules(ruleId, rules);
|
|
782
|
+
const rule = contractRuleId ? rules.get(contractRuleId) : undefined;
|
|
783
|
+
if (!rule || !contractRuleId) {
|
|
774
784
|
throw new Error(`Unknown compliance rule: ${ruleId}`);
|
|
775
785
|
}
|
|
776
786
|
return {
|
|
777
|
-
id: rule.id,
|
|
787
|
+
id: publicProductRuleId(rule.id),
|
|
788
|
+
...(publicProductRuleId(rule.id) !== rule.id ? { contract_rule_id: rule.id } : {}),
|
|
778
789
|
version: rule.version,
|
|
779
790
|
title: rule.title,
|
|
780
791
|
severity: rule.severity,
|
|
@@ -836,6 +847,34 @@ export async function rulesById() {
|
|
|
836
847
|
const rules = (await loadRuleFiles()).flatMap((file) => file.rules);
|
|
837
848
|
return new Map(rules.map((rule) => [rule.id, rule]));
|
|
838
849
|
}
|
|
850
|
+
function checkRuleIdentity(contractRuleId) {
|
|
851
|
+
const binding = productExpectationBindingForSensor(contractRuleId);
|
|
852
|
+
if (!binding) {
|
|
853
|
+
return { ruleId: contractRuleId };
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
ruleId: binding.expectationId,
|
|
857
|
+
contractRuleId,
|
|
858
|
+
validator: {
|
|
859
|
+
platform: binding.platform,
|
|
860
|
+
sensorId: binding.sensorId,
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function resolveRuleIdForContract(ruleId, compliance, rules) {
|
|
865
|
+
if (rules.has(ruleId) && compliance.rules.some((ref) => ref.rule_id === ruleId)) {
|
|
866
|
+
return ruleId;
|
|
867
|
+
}
|
|
868
|
+
const candidates = contractRuleCandidatesForPublicId(ruleId).filter((candidate) => rules.has(candidate) && compliance.rules.some((ref) => ref.rule_id === candidate));
|
|
869
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
870
|
+
}
|
|
871
|
+
function resolveRuleIdForInstalledRules(ruleId, rules) {
|
|
872
|
+
if (rules.has(ruleId)) {
|
|
873
|
+
return ruleId;
|
|
874
|
+
}
|
|
875
|
+
const candidates = contractRuleCandidatesForPublicId(ruleId).filter((candidate) => rules.has(candidate));
|
|
876
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
877
|
+
}
|
|
839
878
|
function ruleRef(rule) {
|
|
840
879
|
return {
|
|
841
880
|
rule_id: rule.id,
|
|
@@ -847,15 +886,21 @@ function ruleRef(rule) {
|
|
|
847
886
|
// Extends ruleRef with the human-readable title for file output (compliance.json,
|
|
848
887
|
// applicableRules in integration plans). Not used for digest computation.
|
|
849
888
|
function ruleRefForFile(rule) {
|
|
850
|
-
|
|
889
|
+
const publicRuleId = publicProductRuleId(rule.id);
|
|
890
|
+
return {
|
|
891
|
+
...ruleRef(rule),
|
|
892
|
+
...(publicRuleId !== rule.id ? { public_rule_id: publicRuleId } : {}),
|
|
893
|
+
title: rule.title,
|
|
894
|
+
};
|
|
851
895
|
}
|
|
852
896
|
// Benchmark-measured friction: agents looped on attest dialect for ~25 min/cell when docs and SDK
|
|
853
897
|
// disagreed on exact invocation syntax (capability-matrix 2026-06, Row 5). Hand them the exact incantation.
|
|
854
898
|
function attestHint(rule) {
|
|
855
899
|
const minConfidence = rule.enforcement.attestation.host_agent_min_confidence ?? "high";
|
|
856
900
|
const fields = rule.enforcement.attestation.evidence_required ?? [];
|
|
901
|
+
const publicRuleId = publicProductRuleId(rule.id);
|
|
857
902
|
return {
|
|
858
|
-
attest_command: `vise attest --rule ${
|
|
903
|
+
attest_command: `vise attest --rule ${publicRuleId} --confidence ${minConfidence} --signer host-agent --evidence-file sp-vise/evidence/${rule.id}.json --rationale "<why this rule is satisfied (or cannot apply) in this codebase>"`,
|
|
859
904
|
evidence_template: Object.fromEntries(fields.map((f) => [f.field, `<${f.description}>`])),
|
|
860
905
|
};
|
|
861
906
|
}
|
|
@@ -878,7 +923,7 @@ function contractDrift(compliance, rules) {
|
|
|
878
923
|
for (const ref of compliance.rules) {
|
|
879
924
|
const rule = rules.get(ref.rule_id);
|
|
880
925
|
if (!rule) {
|
|
881
|
-
results.push({
|
|
926
|
+
results.push({ ...checkRuleIdentity(ref.rule_id), title: ref.rule_id, severity: ref.severity, status: "stale", reason: "Rule is missing from installed Vise." });
|
|
882
927
|
continue;
|
|
883
928
|
}
|
|
884
929
|
const digest = digestRule(rule);
|
|
@@ -888,7 +933,7 @@ function contractDrift(compliance, rules) {
|
|
|
888
933
|
? `Rule digest changed since vise init (v${ref.rule_version} → v${rule.version}; new version is backwards-compatible — re-run vise init; existing attestations will be grandfathered).`
|
|
889
934
|
: `Rule digest changed since vise init (v${ref.rule_version} → v${rule.version}).`;
|
|
890
935
|
results.push({
|
|
891
|
-
|
|
936
|
+
...checkRuleIdentity(ref.rule_id),
|
|
892
937
|
title: rule.title,
|
|
893
938
|
severity: ref.severity,
|
|
894
939
|
status: "stale",
|
package/dist/tools/debug.js
CHANGED
|
@@ -42,7 +42,8 @@ export async function debugIssue(repoPath, errorMessage, options = {}) {
|
|
|
42
42
|
for (const rule of checkResult.rules) {
|
|
43
43
|
if (rule.status === "deterministic-fail" || rule.status === "attestation-needed") {
|
|
44
44
|
// It failed. Check if symptoms match.
|
|
45
|
-
const
|
|
45
|
+
const contractRuleId = rule.contractRuleId ?? rule.ruleId;
|
|
46
|
+
const ruleDef = rulesMap.get(contractRuleId);
|
|
46
47
|
const symptoms = ruleDef?.symptoms || [];
|
|
47
48
|
if (symptoms.some(s => errorMessage.includes(s))) {
|
|
48
49
|
correlatedRules.push({
|
|
@@ -54,7 +55,8 @@ export async function debugIssue(repoPath, errorMessage, options = {}) {
|
|
|
54
55
|
}
|
|
55
56
|
else if (rule.status === "attested") {
|
|
56
57
|
// Check for false-positive attestations
|
|
57
|
-
const
|
|
58
|
+
const contractRuleId = rule.contractRuleId ?? rule.ruleId;
|
|
59
|
+
const ruleDef = rulesMap.get(contractRuleId);
|
|
58
60
|
const symptoms = ruleDef?.symptoms || [];
|
|
59
61
|
if (symptoms.some(s => errorMessage.includes(s))) {
|
|
60
62
|
correlatedRules.push({
|
package/dist/tools/project.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { productFindingIdentity } from "../productExpectations.js";
|
|
3
4
|
import { objectInput, optionalStringField, stringField, textResult } from "../types.js";
|
|
4
5
|
import { findCallExpressions, parse, tryParse, pickObjectProperty, resolveLiteralValue, stripComments } from "./ast.js";
|
|
5
6
|
async function exists(filePath) {
|
|
@@ -1910,7 +1911,7 @@ function statusForFindings(findings) {
|
|
|
1910
1911
|
return findings.length === 0 ? "no-obvious-issues" : "needs-review";
|
|
1911
1912
|
}
|
|
1912
1913
|
function finding(ruleId, severity, message, file, recommendation) {
|
|
1913
|
-
return { ruleId, severity, message, file, recommendation };
|
|
1914
|
+
return { ...productFindingIdentity(ruleId), severity, message, file, recommendation };
|
|
1914
1915
|
}
|
|
1915
1916
|
async function existingFiles(root, relativeFiles) {
|
|
1916
1917
|
const files = [];
|
package/package.json
CHANGED