@codyswann/lisa 3.40.3 → 3.40.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +156 -29
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts +4 -2
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts.map +1 -1
- package/dist/cli/doctor-lisa-owned-artifacts.js +46 -32
- package/dist/cli/doctor-lisa-owned-artifacts.js.map +1 -1
- package/dist/cli/doctor-lisa-owned-universal.d.ts +26 -0
- package/dist/cli/doctor-lisa-owned-universal.d.ts.map +1 -0
- package/dist/cli/doctor-lisa-owned-universal.js +50 -0
- package/dist/cli/doctor-lisa-owned-universal.js.map +1 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +1 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +4 -2
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +1 -1
|
@@ -429,6 +429,66 @@ function currentRepoIdentity(config) {
|
|
|
429
429
|
);
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/**
|
|
433
|
+
* The two contracts this gate can be asked to prove.
|
|
434
|
+
*
|
|
435
|
+
* `trailer` — the DEFAULT — proves that the change names a work item: a
|
|
436
|
+
* `Work-Item:` line exists, is well formed, names the configured tracker's
|
|
437
|
+
* repository or project, matches this worktree's binding, and is the same
|
|
438
|
+
* reference on the commits and on the pull-request body. Every one of those is
|
|
439
|
+
* decidable from the text and the local config, so it needs no credential of
|
|
440
|
+
* any kind.
|
|
441
|
+
*
|
|
442
|
+
* `full` adds the three requirements that need tracker API access, two of them
|
|
443
|
+
* read and one of them WRITE: the item carries a `repo:` label naming this
|
|
444
|
+
* repository, it sits in a claimed lifecycle state, and it carries a verified
|
|
445
|
+
* backlink to this pull request.
|
|
446
|
+
*
|
|
447
|
+
* The default flipped in #2721. `full` was the only contract on offer, so a
|
|
448
|
+
* project unwilling to hand a tracker API key to CI could not satisfy the gate
|
|
449
|
+
* in any form — the traceability requirement and the tracker-integration
|
|
450
|
+
* requirement were one thing, and only the first is what the gate is for.
|
|
451
|
+
*
|
|
452
|
+
* Declared rather than inferred from whichever credentials happen to be
|
|
453
|
+
* present. Inferring it would make the gate's strength a property of the
|
|
454
|
+
* environment: delete a secret and the check quietly asks for less, with the
|
|
455
|
+
* decision recorded nowhere. A level in the config says what the project
|
|
456
|
+
* decided, and `verify-level` prints what actually resolved.
|
|
457
|
+
*/
|
|
458
|
+
const VERIFY_LEVELS = new Set(["trailer", "full"]);
|
|
459
|
+
|
|
460
|
+
/** What a project gets when it says nothing. */
|
|
461
|
+
const DEFAULT_VERIFY = "trailer";
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Resolve how much of the contract to prove.
|
|
465
|
+
*
|
|
466
|
+
* The environment override exists for one caller: CI degrading a project that
|
|
467
|
+
* declared `full` whose tracker credential did not arrive. That used to be a
|
|
468
|
+
* warning and `exit 0` — a required check reporting success having verified
|
|
469
|
+
* nothing at all, an absent trailer included. Degrading to `trailer` still
|
|
470
|
+
* proves the reference, which is the part that was never credential-bound.
|
|
471
|
+
* @param {object} config Merged Lisa config.
|
|
472
|
+
* @returns {string} Either "trailer" or "full".
|
|
473
|
+
*/
|
|
474
|
+
function verifyLevel(config) {
|
|
475
|
+
const override = process.env.LISA_WORK_ITEM_VERIFY;
|
|
476
|
+
const raw =
|
|
477
|
+
override === undefined || override === ""
|
|
478
|
+
? config.workItem?.verify
|
|
479
|
+
: override;
|
|
480
|
+
if (raw === undefined || raw === "") return DEFAULT_VERIFY;
|
|
481
|
+
const value = String(raw).trim().toLowerCase();
|
|
482
|
+
if (!VERIFY_LEVELS.has(value)) {
|
|
483
|
+
throw new TrackingError(
|
|
484
|
+
`Unknown workItem.verify '${raw}'. Expected "trailer" (the default: ` +
|
|
485
|
+
`prove the Work-Item reference, contact no tracker) or "full" (also ` +
|
|
486
|
+
`prove the tracker item's repo scope, claimed state, and PR backlink)`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
return value;
|
|
490
|
+
}
|
|
491
|
+
|
|
432
492
|
function trackerContract(config = readConfig()) {
|
|
433
493
|
const provider = requireString(config.tracker, "tracker").toLowerCase();
|
|
434
494
|
const identityRepo = currentRepoIdentity(config);
|
|
@@ -445,6 +505,7 @@ function trackerContract(config = readConfig()) {
|
|
|
445
505
|
provider,
|
|
446
506
|
repository,
|
|
447
507
|
identityRepo,
|
|
508
|
+
verify: verifyLevel(config),
|
|
448
509
|
lifecycle: lifecycleContract(config, provider),
|
|
449
510
|
repositoryIsIdentity:
|
|
450
511
|
repository.toLowerCase() === `${org}/${githubRepo}`.toLowerCase(),
|
|
@@ -454,6 +515,7 @@ function trackerContract(config = readConfig()) {
|
|
|
454
515
|
return {
|
|
455
516
|
provider,
|
|
456
517
|
identityRepo,
|
|
518
|
+
verify: verifyLevel(config),
|
|
457
519
|
project: requireString(
|
|
458
520
|
config.jira?.project,
|
|
459
521
|
"jira.project"
|
|
@@ -470,6 +532,7 @@ function trackerContract(config = readConfig()) {
|
|
|
470
532
|
return {
|
|
471
533
|
provider,
|
|
472
534
|
identityRepo,
|
|
535
|
+
verify: verifyLevel(config),
|
|
473
536
|
workspace: requireString(config.linear?.workspace, "linear.workspace"),
|
|
474
537
|
teamKey: requireString(
|
|
475
538
|
config.linear?.teamKey,
|
|
@@ -615,40 +678,58 @@ function workItemLines(message) {
|
|
|
615
678
|
}
|
|
616
679
|
|
|
617
680
|
/**
|
|
618
|
-
* The one work item a
|
|
681
|
+
* The one work item a text names, wherever and however often it says so.
|
|
619
682
|
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
683
|
+
* ONE function for commit messages and pull-request bodies, because two of
|
|
684
|
+
* them is how this recurs. #2672 fixed the commit side — read the whole text,
|
|
685
|
+
* accept repeats of the same reference — and left the body side reading every
|
|
686
|
+
* line but rejecting a second one. The same text then answered two different
|
|
687
|
+
* ways depending on which parser saw it, and neither answer explained the
|
|
688
|
+
* other.
|
|
626
689
|
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
690
|
+
* Repeats of the SAME reference pass. They have to on the commit side: Lisa's
|
|
691
|
+
* own prepare-commit-msg hook appends `Work-Item:` to the final trailer block,
|
|
692
|
+
* so a message already carrying the trailer above its attribution block comes
|
|
693
|
+
* back out of that hook with two identical lines. They now pass on the body
|
|
694
|
+
* side too. The old reasoning there — nothing appends a trailer to a body on
|
|
695
|
+
* Lisa's behalf, so a second line means a person wrote two — is simply not
|
|
696
|
+
* true: a body that quotes its own commit message carries the line twice, and
|
|
697
|
+
* bots edit bodies after review. A repeat of the same reference says exactly
|
|
698
|
+
* what one line says, so rejecting it bought tidiness and cost traceability.
|
|
699
|
+
*
|
|
700
|
+
* Two DIFFERENT references is the real ambiguity — which one is this change
|
|
701
|
+
* about? — and it still fails, naming both.
|
|
702
|
+
* @param {string} text Commit message or pull-request body.
|
|
631
703
|
* @param {object} contract Resolved tracker contract.
|
|
704
|
+
* @param {string} subject What is being read, for the message.
|
|
632
705
|
* @returns {string} The canonical work-item reference.
|
|
633
706
|
*/
|
|
634
|
-
function
|
|
635
|
-
const values = workItemLines(
|
|
707
|
+
function soleWorkItem(text, contract, subject) {
|
|
708
|
+
const values = workItemLines(text);
|
|
636
709
|
if (values.length === 0) {
|
|
637
|
-
throw new TrackingError(
|
|
638
|
-
"No Work-Item trailer anywhere in the commit message"
|
|
639
|
-
);
|
|
710
|
+
throw new TrackingError(`No Work-Item trailer anywhere in the ${subject}`);
|
|
640
711
|
}
|
|
641
712
|
const refs = [
|
|
642
713
|
...new Set(values.map(value => canonicalizeRef(value, contract))),
|
|
643
714
|
];
|
|
644
715
|
if (refs.length > 1) {
|
|
645
716
|
throw new TrackingError(
|
|
646
|
-
|
|
717
|
+
`${subject[0].toUpperCase()}${subject.slice(1)} names ${refs.length} different work items (${refs.join(", ")}); it must name exactly one`
|
|
647
718
|
);
|
|
648
719
|
}
|
|
649
720
|
return refs[0];
|
|
650
721
|
}
|
|
651
722
|
|
|
723
|
+
/**
|
|
724
|
+
* The one work item a COMMIT message names.
|
|
725
|
+
* @param {string} message Commit message.
|
|
726
|
+
* @param {object} contract Resolved tracker contract.
|
|
727
|
+
* @returns {string} The canonical work-item reference.
|
|
728
|
+
*/
|
|
729
|
+
function exactWorkItem(message, contract = trackerContract()) {
|
|
730
|
+
return soleWorkItem(message, contract, "commit message");
|
|
731
|
+
}
|
|
732
|
+
|
|
652
733
|
function messageSubject(message) {
|
|
653
734
|
return message.split(/\r?\n/, 1)[0] ?? "";
|
|
654
735
|
}
|
|
@@ -1156,6 +1237,11 @@ function linearIssue(ref, contract) {
|
|
|
1156
1237
|
* @returns {object|undefined} The live item, or undefined when unreachable.
|
|
1157
1238
|
*/
|
|
1158
1239
|
function validateLive(ref, contract = trackerContract()) {
|
|
1240
|
+
// Not a degradation and not a skip — under `trailer` the tracker's answer is
|
|
1241
|
+
// no part of the contract this project asked to have proved, so there is
|
|
1242
|
+
// nothing to report and nothing to contact. A warning here would be noise on
|
|
1243
|
+
// every commit in a project that made a deliberate choice.
|
|
1244
|
+
if (contract.verify !== "full") return undefined;
|
|
1159
1245
|
try {
|
|
1160
1246
|
if (contract.provider === "github") return githubIssue(ref, contract);
|
|
1161
1247
|
if (contract.provider === "jira") return jiraIssue(ref, contract);
|
|
@@ -1678,13 +1764,17 @@ function parsePushLines(input, remote) {
|
|
|
1678
1764
|
* @param {object} contract Resolved tracker contract.
|
|
1679
1765
|
* @returns {string} The canonical work-item reference.
|
|
1680
1766
|
*/
|
|
1767
|
+
/**
|
|
1768
|
+
* The one work item a PULL-REQUEST BODY names.
|
|
1769
|
+
*
|
|
1770
|
+
* Same rule as the commit message, through the same function. See
|
|
1771
|
+
* `soleWorkItem` for why the two used to disagree and why they no longer do.
|
|
1772
|
+
* @param {string} body Pull-request body.
|
|
1773
|
+
* @param {object} contract Resolved tracker contract.
|
|
1774
|
+
* @returns {string} The canonical work-item reference.
|
|
1775
|
+
*/
|
|
1681
1776
|
function prWorkItem(body, contract) {
|
|
1682
|
-
|
|
1683
|
-
if (matches.length !== 1)
|
|
1684
|
-
throw new TrackingError(
|
|
1685
|
-
`Pull request must contain exactly one Work-Item line; found ${matches.length}`
|
|
1686
|
-
);
|
|
1687
|
-
return canonicalizeRef(matches[0], contract);
|
|
1777
|
+
return soleWorkItem(body, contract, "pull request body");
|
|
1688
1778
|
}
|
|
1689
1779
|
|
|
1690
1780
|
/**
|
|
@@ -1814,7 +1904,11 @@ function validatePrData(outcome, prUrl, prBody) {
|
|
|
1814
1904
|
scope: IN_THIS_PR,
|
|
1815
1905
|
});
|
|
1816
1906
|
const ref = commitRef ?? bodyRef;
|
|
1817
|
-
|
|
1907
|
+
// Requirement 4 belongs to `full` alone: it needs tracker WRITE access, and
|
|
1908
|
+
// a project that keeps no tracker credentials cannot ever satisfy it. The
|
|
1909
|
+
// reference checks above are what stays, and they still refuse everything
|
|
1910
|
+
// that is genuinely untraceable.
|
|
1911
|
+
if (ref && contract.verify === "full") {
|
|
1818
1912
|
const before = findings.length;
|
|
1819
1913
|
collect(findings, OUTSIDE_THIS_PR, () =>
|
|
1820
1914
|
assertBacklink(ref, prUrl, contract, commitRef ? result.issue : undefined)
|
|
@@ -1825,6 +1919,22 @@ function validatePrData(outcome, prUrl, prBody) {
|
|
|
1825
1919
|
if (findings.length > 0) throw new TrackingError(requirementReport(findings));
|
|
1826
1920
|
}
|
|
1827
1921
|
|
|
1922
|
+
/**
|
|
1923
|
+
* What a successful run actually proved, in its own words.
|
|
1924
|
+
*
|
|
1925
|
+
* A `trailer` run that printed "and tracker backlink" would be claiming a
|
|
1926
|
+
* check it deliberately did not make — the same class of untruth as a gate
|
|
1927
|
+
* that reports success having verified nothing, said in the success line
|
|
1928
|
+
* instead of the exit code.
|
|
1929
|
+
* @param {object} contract Resolved tracker contract.
|
|
1930
|
+
* @returns {string} The requirements this run proved.
|
|
1931
|
+
*/
|
|
1932
|
+
function provedHere(contract) {
|
|
1933
|
+
return contract.verify === "full"
|
|
1934
|
+
? "PR body, and tracker backlink"
|
|
1935
|
+
: 'and PR body (workItem.verify is "trailer": the tracker was not contacted)';
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1828
1938
|
function currentRepository() {
|
|
1829
1939
|
if (process.env.GITHUB_REPOSITORY) return process.env.GITHUB_REPOSITORY;
|
|
1830
1940
|
const result = run("gh", ["repo", "view", "--json", "nameWithOwner"], {
|
|
@@ -1954,7 +2064,7 @@ function validatePush(args) {
|
|
|
1954
2064
|
}
|
|
1955
2065
|
validatePrData(outcome, pr.url, pr.body);
|
|
1956
2066
|
console.log(
|
|
1957
|
-
`WORK_ITEM_TRACKING_OK ${outcome.result.relevant} commit(s),
|
|
2067
|
+
`WORK_ITEM_TRACKING_OK ${outcome.result.relevant} commit(s), ${provedHere(outcome.result.contract)}`
|
|
1958
2068
|
);
|
|
1959
2069
|
}
|
|
1960
2070
|
|
|
@@ -1980,14 +2090,26 @@ function validatePr(args) {
|
|
|
1980
2090
|
const pr = bodyFile
|
|
1981
2091
|
? { url: suppliedUrl, body: readFileSync(bodyFile, "utf8") }
|
|
1982
2092
|
: fetched && { ...fetched, url: suppliedUrl ?? fetched.url };
|
|
1983
|
-
if (!pr
|
|
2093
|
+
if (!pr) {
|
|
2094
|
+
throw new TrackingError(
|
|
2095
|
+
"validate-pr requires --pr-number or --body-file, and an accessible GitHub PR"
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
// The URL identifies the pull request a BACKLINK must point at, so it is
|
|
2099
|
+
// required exactly when a backlink is. Demanding it under `trailer` would
|
|
2100
|
+
// reintroduce a credentialled step into the credential-free path for no
|
|
2101
|
+
// check that consumes it.
|
|
2102
|
+
if (
|
|
2103
|
+
!pr.url &&
|
|
2104
|
+
(outcome.result?.contract ?? trackerContract()).verify === "full"
|
|
2105
|
+
) {
|
|
1984
2106
|
throw new TrackingError(
|
|
1985
|
-
"validate-pr requires --pr-
|
|
2107
|
+
"validate-pr requires --pr-url/--url alongside --body-file so the tracker backlink can be verified"
|
|
1986
2108
|
);
|
|
1987
2109
|
}
|
|
1988
2110
|
validatePrData(outcome, pr.url, pr.body);
|
|
1989
2111
|
console.log(
|
|
1990
|
-
`WORK_ITEM_TRACKING_OK ${outcome.result.relevant} commit(s),
|
|
2112
|
+
`WORK_ITEM_TRACKING_OK ${outcome.result.relevant} commit(s), ${provedHere(outcome.result.contract)}`
|
|
1991
2113
|
);
|
|
1992
2114
|
}
|
|
1993
2115
|
|
|
@@ -2037,12 +2159,17 @@ function main() {
|
|
|
2037
2159
|
return console.log("work-item binding cleared");
|
|
2038
2160
|
}
|
|
2039
2161
|
if (command === "backlink") return backlink(args);
|
|
2162
|
+
// One place resolves the level, and everything else asks. The CI job needs
|
|
2163
|
+
// it to decide whether a missing tracker credential is a problem, and a
|
|
2164
|
+
// second implementation of the precedence rules in shell is exactly the
|
|
2165
|
+
// two-parsers drift this change exists to remove.
|
|
2166
|
+
if (command === "verify-level") return console.log(trackerContract().verify);
|
|
2040
2167
|
if (command === "prepare-commit-msg") return prepareCommitMessage(args);
|
|
2041
2168
|
if (command === "validate-commit") return validateCommit(args);
|
|
2042
2169
|
if (command === "validate-push") return validatePush(args);
|
|
2043
2170
|
if (command === "validate-pr") return validatePr(args);
|
|
2044
2171
|
throw new TrackingError(
|
|
2045
|
-
"Usage: lisa-work-item.mjs link|current|attach-branch|clear|backlink|prepare-commit-msg|validate-commit|validate-push|validate-pr" +
|
|
2172
|
+
"Usage: lisa-work-item.mjs link|current|attach-branch|clear|verify-level|backlink|prepare-commit-msg|validate-commit|validate-push|validate-pr" +
|
|
2046
2173
|
"\n(`bind` is accepted as an alias for `link`, but some agent harnesses refuse the token `bind` in a command line.)"
|
|
2047
2174
|
);
|
|
2048
2175
|
}
|
|
@@ -15,8 +15,10 @@ interface ArtifactCheck {
|
|
|
15
15
|
* nobody can account for is worth a line either way, and staying silent about a
|
|
16
16
|
* downstream edit would let a project quietly swap a guard for a stub.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* A third finding covers absence, but only for the universal tree. A missing
|
|
19
|
+
* stack-specific artifact means that stack does not apply here, which is not
|
|
20
|
+
* drift. A missing universal one means apply has not run, and the CI gate that
|
|
21
|
+
* calls it is not running at all.
|
|
20
22
|
* @param targetPath - Project path to inspect
|
|
21
23
|
* @param lisaRoot - Installed Lisa package root (injected by tests)
|
|
22
24
|
* @param ledger - Known-good hashes, defaulting to Lisa's shipping history
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-lisa-owned-artifacts.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor-lisa-owned-artifacts.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AA0BnE,wEAAwE;AACxE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AA8FD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAA0B,EACpC,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
|
|
@@ -18,16 +18,13 @@
|
|
|
18
18
|
import { readFile } from "node:fs/promises";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
|
-
import * as fse from "fs-extra";
|
|
22
21
|
import { PROJECT_TYPE_ORDER } from "../core/config.js";
|
|
23
22
|
import { classifyHostCopy, mayRefreshLisaOwned, } from "../core/lisa-owned-provenance.js";
|
|
24
|
-
import { isLisaOwnedTemplate } from "../core/lisa-owned-templates.js";
|
|
25
23
|
import { isLisaSourceRepo } from "../core/self-apply.js";
|
|
26
|
-
import { listFilesRecursive } from "../utils/file-operations.js";
|
|
27
24
|
import { matchesAnyPattern, parseIgnorePatterns, } from "../utils/ignore-patterns.js";
|
|
28
25
|
import { describeResolvableCopies, } from "./doctor-lisa-owned-artifact-copies.js";
|
|
26
|
+
import { shippedByStack, UNIVERSAL_STACK, universalDestinations, } from "./doctor-lisa-owned-universal.js";
|
|
29
27
|
const CHECK_NAME = "Lisa enforcement artifacts current?";
|
|
30
|
-
const COPY_OVERWRITE = "copy-overwrite";
|
|
31
28
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
29
|
/**
|
|
33
30
|
* Quoted relative module specifiers — `import`/`export from`, `require()`, and
|
|
@@ -42,23 +39,6 @@ const RELATIVE_SPECIFIER = /["'`](\.\.?\/[^"'`\n]{1,4096})["'`]/g;
|
|
|
42
39
|
function defaultLisaRoot() {
|
|
43
40
|
return path.resolve(__dirname, "..", "..");
|
|
44
41
|
}
|
|
45
|
-
/**
|
|
46
|
-
* List the Lisa-owned artifacts one stack's copy-overwrite tree ships.
|
|
47
|
-
* @param lisaRoot - Installed Lisa package root
|
|
48
|
-
* @param type - Stack directory name
|
|
49
|
-
* @returns Destination/source pairs shipped by that stack
|
|
50
|
-
*/
|
|
51
|
-
async function shippedByStack(lisaRoot, type) {
|
|
52
|
-
const directory = path.join(lisaRoot, type, COPY_OVERWRITE);
|
|
53
|
-
if (!(await fse.pathExists(directory)))
|
|
54
|
-
return [];
|
|
55
|
-
return (await listFilesRecursive(directory))
|
|
56
|
-
.map(source => [
|
|
57
|
-
path.relative(directory, source).split(path.sep).join("/"),
|
|
58
|
-
source,
|
|
59
|
-
])
|
|
60
|
-
.filter(([destination]) => isLisaOwnedTemplate(destination));
|
|
61
|
-
}
|
|
62
42
|
/**
|
|
63
43
|
* Collect every shipped Lisa-owned artifact, keyed by its destination path.
|
|
64
44
|
*
|
|
@@ -69,7 +49,7 @@ async function shippedByStack(lisaRoot, type) {
|
|
|
69
49
|
* @returns Destination path to the shipped source files that produce it
|
|
70
50
|
*/
|
|
71
51
|
async function shippedArtifacts(lisaRoot) {
|
|
72
|
-
const shipped = (await Promise.all([
|
|
52
|
+
const shipped = (await Promise.all([UNIVERSAL_STACK, ...PROJECT_TYPE_ORDER].map(async (type) => shippedByStack(lisaRoot, type)))).flat();
|
|
73
53
|
const destinations = [
|
|
74
54
|
...new Set(shipped.map(([destination]) => destination)),
|
|
75
55
|
];
|
|
@@ -123,8 +103,10 @@ function reExportsShippedTemplate(installed, installedPath, sources) {
|
|
|
123
103
|
* nobody can account for is worth a line either way, and staying silent about a
|
|
124
104
|
* downstream edit would let a project quietly swap a guard for a stub.
|
|
125
105
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
106
|
+
* A third finding covers absence, but only for the universal tree. A missing
|
|
107
|
+
* stack-specific artifact means that stack does not apply here, which is not
|
|
108
|
+
* drift. A missing universal one means apply has not run, and the CI gate that
|
|
109
|
+
* calls it is not running at all.
|
|
128
110
|
* @param targetPath - Project path to inspect
|
|
129
111
|
* @param lisaRoot - Installed Lisa package root (injected by tests)
|
|
130
112
|
* @param ledger - Known-good hashes, defaulting to Lisa's shipping history
|
|
@@ -132,6 +114,7 @@ function reExportsShippedTemplate(installed, installedPath, sources) {
|
|
|
132
114
|
*/
|
|
133
115
|
export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisaRoot(), ledger) {
|
|
134
116
|
const shipped = await shippedArtifacts(lisaRoot);
|
|
117
|
+
const universal = await universalDestinations(lisaRoot);
|
|
135
118
|
if (shipped.size === 0) {
|
|
136
119
|
return {
|
|
137
120
|
name: CHECK_NAME,
|
|
@@ -146,7 +129,7 @@ export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisa
|
|
|
146
129
|
// unless the target's package.json is Lisa itself. A host must not be able to
|
|
147
130
|
// swap a guard for a thin re-export and have doctor call it current.
|
|
148
131
|
const selfHost = await isLisaSourceRepo(targetPath);
|
|
149
|
-
const results = await Promise.all([...shipped].map(async ([destination, sources]) => assessArtifact(targetPath, lisaRoot, destination, sources, ignorePatterns, selfHost, ledger)));
|
|
132
|
+
const results = await Promise.all([...shipped].map(async ([destination, sources]) => assessArtifact(targetPath, lisaRoot, destination, sources, ignorePatterns, selfHost, universal.has(destination), ledger)));
|
|
150
133
|
const assessments = results.filter((item) => item !== undefined);
|
|
151
134
|
return summarise(assessments
|
|
152
135
|
.map(item => item.finding)
|
|
@@ -162,19 +145,16 @@ export async function checkLisaOwnedArtifacts(targetPath, lisaRoot = defaultLisa
|
|
|
162
145
|
* @param sources - Absolute shipped package variants
|
|
163
146
|
* @param ignorePatterns - Parsed .lisaignore patterns
|
|
164
147
|
* @param selfHost - Whether the target is Lisa's own source repository
|
|
148
|
+
* @param universal - Whether every project receives this destination
|
|
165
149
|
* @param ledger - Known-good hashes, defaulting to Lisa's shipping history
|
|
166
|
-
* @returns Per-artifact assessment, or undefined when
|
|
150
|
+
* @returns Per-artifact assessment, or undefined when nothing is reportable
|
|
167
151
|
*/
|
|
168
|
-
async function assessArtifact(targetPath, lisaRoot, destination, sources, ignorePatterns, selfHost, ledger) {
|
|
152
|
+
async function assessArtifact(targetPath, lisaRoot, destination, sources, ignorePatterns, selfHost, universal, ledger) {
|
|
169
153
|
const installedPath = path.join(targetPath, destination);
|
|
170
154
|
const installed = await readFile(installedPath).catch(() => undefined);
|
|
171
155
|
const ignored = matchesAnyPattern(destination, ignorePatterns);
|
|
172
156
|
if (installed === undefined) {
|
|
173
|
-
return ignored
|
|
174
|
-
? {
|
|
175
|
-
finding: [destination, "ignored"],
|
|
176
|
-
}
|
|
177
|
-
: undefined;
|
|
157
|
+
return assessAbsent(destination, ignored, universal, selfHost);
|
|
178
158
|
}
|
|
179
159
|
const multiCopy = await describeResolvableCopies(lisaRoot, destination, installed, sources);
|
|
180
160
|
if (ignored)
|
|
@@ -188,6 +168,35 @@ async function assessArtifact(targetPath, lisaRoot, destination, sources, ignore
|
|
|
188
168
|
const outdated = await isProvablyStale(installed, destination, sources, ledger);
|
|
189
169
|
return withMultiCopy([destination, outdated ? "stale" : "modified"], multiCopy);
|
|
190
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Assess a Lisa-owned artifact the project does not have.
|
|
173
|
+
*
|
|
174
|
+
* An absent stack-specific artifact means that stack does not apply here, which
|
|
175
|
+
* is not drift — that exemption is why absence used to report nothing at all.
|
|
176
|
+
* It over-reached: a universal artifact has no stack it does not apply to, so
|
|
177
|
+
* its absence means apply never ran and the CI gate that calls it is not
|
|
178
|
+
* running. Measured on `scripts/lisa-floor-collisions.mjs` (#2731), whose job
|
|
179
|
+
* exited 0 on the missing script — green, having examined nothing, with doctor
|
|
180
|
+
* silent too. Fixing only the job turns a silent pass into a red nobody has
|
|
181
|
+
* been told how to clear.
|
|
182
|
+
*
|
|
183
|
+
* Never for Lisa's own repository: it is the source of these artifacts, not a
|
|
184
|
+
* consumer, and installs only the few it runs on itself, so "apply never ran"
|
|
185
|
+
* is a category error there.
|
|
186
|
+
* @param destination - Project-relative artifact destination
|
|
187
|
+
* @param ignored - Whether .lisaignore names this destination
|
|
188
|
+
* @param universal - Whether every project receives this destination
|
|
189
|
+
* @param selfHost - Whether the target is Lisa's own source repository
|
|
190
|
+
* @returns Per-artifact assessment, or undefined when absence proves nothing
|
|
191
|
+
*/
|
|
192
|
+
function assessAbsent(destination, ignored, universal, selfHost) {
|
|
193
|
+
if (ignored) {
|
|
194
|
+
return { finding: [destination, "ignored"] };
|
|
195
|
+
}
|
|
196
|
+
return universal && !selfHost
|
|
197
|
+
? { finding: [destination, "missing"] }
|
|
198
|
+
: undefined;
|
|
199
|
+
}
|
|
191
200
|
/**
|
|
192
201
|
* Attach optional multi-copy provenance without materialising undefined fields.
|
|
193
202
|
* @param finding - Artifact finding tuple
|
|
@@ -211,10 +220,12 @@ function summarise(findings, multiCopies) {
|
|
|
211
220
|
const stale = pick("stale");
|
|
212
221
|
const modified = pick("modified");
|
|
213
222
|
const ignored = pick("ignored");
|
|
223
|
+
const missing = pick("missing");
|
|
214
224
|
const copyDisagreements = multiCopies.filter(copy => copy.disagrees);
|
|
215
225
|
const copyReports = renderCopyReports(multiCopies);
|
|
216
226
|
const warningFree = stale.length === 0 &&
|
|
217
227
|
modified.length === 0 &&
|
|
228
|
+
missing.length === 0 &&
|
|
218
229
|
copyDisagreements.length === 0;
|
|
219
230
|
if (warningFree) {
|
|
220
231
|
// Named rather than folded into the pass. A guard excluded by
|
|
@@ -231,6 +242,9 @@ function summarise(findings, multiCopies) {
|
|
|
231
242
|
ignored.length > 0
|
|
232
243
|
? `${ignored.length} not assessed (.lisaignore): ${ignored.join(", ")}`
|
|
233
244
|
: "",
|
|
245
|
+
missing.length > 0
|
|
246
|
+
? `Lisa-owned guards every project receives are not installed, so the CI gates that call them run against nothing (run \`npx lisa apply .\` to install them): ${missing.join(", ")}`
|
|
247
|
+
: "",
|
|
234
248
|
stale.length > 0
|
|
235
249
|
? `Outdated Lisa-owned guards (run \`npx lisa apply .\` to refresh): ${stale.join(", ")}`
|
|
236
250
|
: "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-lisa-owned-artifacts.js","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor-lisa-owned-artifacts.js","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Directory name of the strategy tree these artifacts are shipped from. */
|
|
2
|
+
export declare const COPY_OVERWRITE = "copy-overwrite";
|
|
3
|
+
/**
|
|
4
|
+
* The stack tree every project receives regardless of detected type.
|
|
5
|
+
*
|
|
6
|
+
* `Lisa.processProjectType("all")` runs unconditionally and every downstream
|
|
7
|
+
* loop is `["all", ...detectedTypes]`, so an artifact shipped here has no stack
|
|
8
|
+
* it legitimately does not apply to.
|
|
9
|
+
*/
|
|
10
|
+
export declare const UNIVERSAL_STACK = "all";
|
|
11
|
+
/** One shipped Lisa-owned artifact and the destination it installs to. */
|
|
12
|
+
export type ShippedArtifact = readonly [destination: string, source: string];
|
|
13
|
+
/**
|
|
14
|
+
* List the Lisa-owned artifacts one stack's copy-overwrite tree ships.
|
|
15
|
+
* @param lisaRoot - Installed Lisa package root
|
|
16
|
+
* @param type - Stack directory name
|
|
17
|
+
* @returns Destination/source pairs shipped by that stack
|
|
18
|
+
*/
|
|
19
|
+
export declare function shippedByStack(lisaRoot: string, type: string): Promise<readonly ShippedArtifact[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Destinations shipped by the universal tree, which every project receives.
|
|
22
|
+
* @param lisaRoot - Installed Lisa package root
|
|
23
|
+
* @returns Destination paths no project can legitimately be without
|
|
24
|
+
*/
|
|
25
|
+
export declare function universalDestinations(lisaRoot: string): Promise<ReadonlySet<string>>;
|
|
26
|
+
//# sourceMappingURL=doctor-lisa-owned-universal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-lisa-owned-universal.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-universal.ts"],"names":[],"mappings":"AAgBA,4EAA4E;AAC5E,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE7E;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAYrC;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAM9B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which Lisa-owned artifacts every project receives, whatever stack it is.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from `doctor-lisa-owned-artifacts.ts` for the max-lines budget,
|
|
5
|
+
* the same way the safety-net fixtures are split. The distinction it encodes is
|
|
6
|
+
* small but load-bearing: absence of a STACK artifact proves nothing, because
|
|
7
|
+
* that stack may simply not apply here, while absence of a UNIVERSAL one means
|
|
8
|
+
* apply never ran and the CI gate that calls it is not running at all.
|
|
9
|
+
* @module cli/doctor-lisa-owned-universal
|
|
10
|
+
*/
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import * as fse from "fs-extra";
|
|
13
|
+
import { isLisaOwnedTemplate } from "../core/lisa-owned-templates.js";
|
|
14
|
+
import { listFilesRecursive } from "../utils/file-operations.js";
|
|
15
|
+
/** Directory name of the strategy tree these artifacts are shipped from. */
|
|
16
|
+
export const COPY_OVERWRITE = "copy-overwrite";
|
|
17
|
+
/**
|
|
18
|
+
* The stack tree every project receives regardless of detected type.
|
|
19
|
+
*
|
|
20
|
+
* `Lisa.processProjectType("all")` runs unconditionally and every downstream
|
|
21
|
+
* loop is `["all", ...detectedTypes]`, so an artifact shipped here has no stack
|
|
22
|
+
* it legitimately does not apply to.
|
|
23
|
+
*/
|
|
24
|
+
export const UNIVERSAL_STACK = "all";
|
|
25
|
+
/**
|
|
26
|
+
* List the Lisa-owned artifacts one stack's copy-overwrite tree ships.
|
|
27
|
+
* @param lisaRoot - Installed Lisa package root
|
|
28
|
+
* @param type - Stack directory name
|
|
29
|
+
* @returns Destination/source pairs shipped by that stack
|
|
30
|
+
*/
|
|
31
|
+
export async function shippedByStack(lisaRoot, type) {
|
|
32
|
+
const directory = path.join(lisaRoot, type, COPY_OVERWRITE);
|
|
33
|
+
if (!(await fse.pathExists(directory)))
|
|
34
|
+
return [];
|
|
35
|
+
return (await listFilesRecursive(directory))
|
|
36
|
+
.map(source => [
|
|
37
|
+
path.relative(directory, source).split(path.sep).join("/"),
|
|
38
|
+
source,
|
|
39
|
+
])
|
|
40
|
+
.filter(([destination]) => isLisaOwnedTemplate(destination));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Destinations shipped by the universal tree, which every project receives.
|
|
44
|
+
* @param lisaRoot - Installed Lisa package root
|
|
45
|
+
* @returns Destination paths no project can legitimately be without
|
|
46
|
+
*/
|
|
47
|
+
export async function universalDestinations(lisaRoot) {
|
|
48
|
+
return new Set((await shippedByStack(lisaRoot, UNIVERSAL_STACK)).map(([destination]) => destination));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=doctor-lisa-owned-universal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-lisa-owned-universal.js","sourceRoot":"","sources":["../../src/cli/doctor-lisa-owned-universal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAKrC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,IAAY;IAEZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClD,OAAO,CAAC,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;SACzC,GAAG,CACF,MAAM,CAAC,EAAE,CACP;QACE,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1D,MAAM;KACE,CACb;SACA,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAAgB;IAEhB,OAAO,IAAI,GAAG,CACZ,CAAC,MAAM,cAAc,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,CACnD,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,WAAW,CAC/B,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAuYjC,CAAC"}
|
|
@@ -356,6 +356,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
|
|
|
356
356
|
"9223856be4618fb1e3e731b259ae5bf5328decc322f0b4a0d0fd18d6b12cd45a",
|
|
357
357
|
"96a87c131606b3edd43e52485e68f8ffbf5c528cb4beb7a1185263519a9632ce",
|
|
358
358
|
"9b3dda4a697bc23d8e71a0f00cf6204c80f5670724b843764af4fe8ecd9b2df2",
|
|
359
|
+
"9e1db1f28cc7ee4b66b08c2fd2ee0ebf98054786d5902245941641e98783bf47",
|
|
359
360
|
"ab0dede91c5c8a07984ae13aa48ec70cdcd2fa691b59617bc4b57998dc79a51c",
|
|
360
361
|
"b4e8aea72219f2c568429686a2ff1acd0da75ca26f244a66f3c4334297c8cd8e",
|
|
361
362
|
"c78df9d5f0f3fc388ff60a7db5898478a2a4d6d7325833f10384b5a8c62bb34a",
|