@codyswann/lisa 4.87.0 → 4.87.2
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/lib/completion-proof-content.mjs +20 -6
- package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +10 -8
- package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +8 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.js +10 -10
- package/package.json +4 -4
- 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-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa/.codex-plugin/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- package/plugins/lisa/hooks/block-blind-automerge.sh +10 -8
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- package/plugins/lisa-agy/hooks/block-blind-automerge.sh +10 -8
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa-agy/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- 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-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +10 -8
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa-copilot/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +10 -8
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa-cursor/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/hooks/block-blind-automerge.sh +10 -8
- package/plugins/lisa-devin/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/lisa-devin/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- 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-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-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-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-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-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-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-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-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-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-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-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-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-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-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/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-blind-automerge.sh +10 -8
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +93 -29
- package/plugins/src/base/skills/lisa-pull-request-review/SKILL.md +8 -0
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +2 -2
- package/plugins/src/base/skills/lisa-session-manager/scripts/ledger-questions.mjs +9 -4
- package/scripts/lisa-ruleset-payload.mjs +17 -2
|
@@ -393,15 +393,16 @@ declared, arm normally (the scope rule above). Where one IS declared, absent a
|
|
|
393
393
|
description that positively states a review ran, treat the context as vacuous.
|
|
394
394
|
Fail closed: absence of evidence is not evidence of a review.
|
|
395
395
|
|
|
396
|
-
#### Vacuous
|
|
396
|
+
#### Vacuous, transient or standing: the response is the same
|
|
397
397
|
|
|
398
|
-
A vacuous context is
|
|
399
|
-
|
|
400
|
-
|
|
398
|
+
A vacuous context is never something to wait out. Waiting looks like progress
|
|
399
|
+
but it is a stall, and on a repository where the state never clears it waits
|
|
400
|
+
forever. Some vacuities clear on their own and some do not. **That changes what
|
|
401
|
+
the report says, not what you do:**
|
|
401
402
|
|
|
402
403
|
| description | vacuous? | clears on its own? |
|
|
403
404
|
| --- | --- | --- |
|
|
404
|
-
| `Review rate limited` | yes |
|
|
405
|
+
| `Review rate limited` (and decorated forms such as `Review rate limited (retry in 12m)`) | yes | eventually — a throughput window of unknown length |
|
|
405
406
|
| `Review skipped: manual review required for this OSS repository` | yes | **no** — a standing vendor policy for public repositories |
|
|
406
407
|
| `Review skipped: N files exceed the limit of M` | yes | **no** — not without splitting the pull request |
|
|
407
408
|
| `Review skipped` (bare, no stated reason) | yes | **unknown — treat as standing** |
|
|
@@ -410,17 +411,33 @@ A bare skip carries no machine-readable reason, so nothing distinguishes it from
|
|
|
410
411
|
the standing forms. Requiring a stated reason before treating a skip as vacuous
|
|
411
412
|
is how the commonest form gets missed.
|
|
412
413
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
414
|
+
**Every row, the rate limit included: do not wait, and do not re-request.** Do
|
|
415
|
+
not arm. Go straight to the watch loop, run the local adversarial review over the
|
|
416
|
+
PR diff at the current head **now**, post it with the substitute marker (step
|
|
417
|
+
(d), "In case 3, substitute"), and keep driving. Fix what it finds, substitute
|
|
418
|
+
again on each new head, and let step (d) merge when the vacuous context is the
|
|
419
|
+
sole remaining gate.
|
|
420
|
+
|
|
421
|
+
The rate limit used to get a wait-the-window-out-and-re-request-once exception.
|
|
422
|
+
That exception is removed, for three reasons:
|
|
423
|
+
|
|
424
|
+
- **The window is not short.** Agents waiting it out stalled for an hour or more
|
|
425
|
+
per pull request, across a whole portfolio of repositories, and nothing about
|
|
426
|
+
the wait made the merge safer than a substitute review would.
|
|
427
|
+
- **Re-requesting does not work under a throttle.** Measured on #3220, a single
|
|
428
|
+
explicit re-request was acknowledged, went `Review in progress`, and settled
|
|
429
|
+
back at `Review rate limited` two seconds later having reviewed nothing. That
|
|
430
|
+
minted a second hollow green.
|
|
431
|
+
- **Re-requesting destroys evidence.** It overwrites the existing status (see
|
|
432
|
+
"Never re-request a review to 'refresh' it" in step (d)), so the exception
|
|
433
|
+
contradicted a rule this file states with no exceptions.
|
|
434
|
+
|
|
435
|
+
If the reviewer later reviews on its own, nothing special happens. Its threads
|
|
436
|
+
are review threads like any other, and step (d) handles them. A `CHANGES_REQUESTED`
|
|
437
|
+
it leaves is a real blocker (step (e)), and the substitute never overrides it.
|
|
438
|
+
If it now proves work, arm, per the table above. That is a re-classification
|
|
439
|
+
the watch loop makes on what it reads anyway. It is never a reason to wait or
|
|
440
|
+
re-request.
|
|
424
441
|
|
|
425
442
|
#### A latch somebody else armed
|
|
426
443
|
|
|
@@ -644,17 +661,24 @@ step (e) first — which discriminates a live objection from a stranded one by
|
|
|
644
661
|
unresolved thread count, not by `reviewDecision` — and arm afterwards.
|
|
645
662
|
|
|
646
663
|
**Two ruleset settings decide whether a standing verdict ever clears itself, and
|
|
647
|
-
|
|
648
|
-
than assuming, with `gh api repos/<owner>/<repo>/
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
664
|
+
neither one clears a change request.** Verify them for the repo you are in
|
|
665
|
+
rather than assuming, with `gh api repos/<owner>/<repo>/rules/branches/<base>`
|
|
666
|
+
(the rules in force on the base branch, with their parameters; the
|
|
667
|
+
`gh api repos/<owner>/<repo>/rulesets` list carries no rule parameters):
|
|
668
|
+
|
|
669
|
+
- `dismiss_stale_reviews_on_push` dismisses stale **approvals** only. The `base`
|
|
670
|
+
ruleset Lisa generates defaults it to `true` when
|
|
671
|
+
`policy.protect.dismiss_stale_reviews` is undeclared, and keeps a declared
|
|
672
|
+
value (CodySwannGT/lisa#4574), but a
|
|
673
|
+
`CHANGES_REQUESTED` review made on an older head stays in force after a push
|
|
674
|
+
under either setting (measured on a probe repository on 2026-11-01: the push
|
|
675
|
+
changed the diff and `reviewDecision` stayed `CHANGES_REQUESTED`), so
|
|
676
|
+
**pushing the fix never clears it**.
|
|
653
677
|
- `required_approving_review_count: 0` — no approval is required to merge, so a
|
|
654
678
|
standing `CHANGES_REQUESTED` is not holding a slot for a review that would
|
|
655
679
|
otherwise arrive. Nothing is scheduled to clear it.
|
|
656
680
|
|
|
657
|
-
|
|
681
|
+
Where both hold, the block is **indefinite, not slow**. Waiting is not a
|
|
658
682
|
strategy; something has to act.
|
|
659
683
|
|
|
660
684
|
**Unresolved threads block independently of the verdict.** This repository sets
|
|
@@ -1048,10 +1072,11 @@ configured reviewed-when phrase says what "did work" looks like for this
|
|
|
1048
1072
|
project.
|
|
1049
1073
|
|
|
1050
1074
|
Prove the signal from the commit status API as above; a merely pending or queued
|
|
1051
|
-
check is not proof of anything and must keep polling. **A
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1075
|
+
check is not proof of anything and must keep polling. **A settled vacuity, the
|
|
1076
|
+
rate limit included, arrives here immediately.** There is no wait to spend first
|
|
1077
|
+
and no re-request to try: a rate limit's window is of unknown length, and
|
|
1078
|
+
waiting it out stalls the pull request without reading a line of the diff. The
|
|
1079
|
+
substitute is what makes the merge safe, not the passage of time.
|
|
1055
1080
|
|
|
1056
1081
|
Once the signal is explicit, merge directly with `gh pr merge <pr>
|
|
1057
1082
|
--<merge_method>` (pass `--admin` only if branch protection lists the vacuous
|
|
@@ -1172,6 +1197,34 @@ Deleting the fallback outright would over-correct: it is the only remedy a lane
|
|
|
1172
1197
|
without dismissal permission has, and that permission is not universal. This
|
|
1173
1198
|
narrows it to the case that costs nothing.
|
|
1174
1199
|
|
|
1200
|
+
**A stale bot verdict under a rate limit: substitute, then stop and report.**
|
|
1201
|
+
Under a rate limit or an exhausted review allowance, a bot's old
|
|
1202
|
+
`CHANGES_REQUESTED` on an outdated head can end up as the only thing blocking
|
|
1203
|
+
the merge. The bot cannot post a fresh review to replace it, so the verdict
|
|
1204
|
+
will not clear by itself while the throttle lasts. This was measured on a
|
|
1205
|
+
caller repo in the portfolio: every check green, zero unresolved threads, and
|
|
1206
|
+
the bot's verdict from an earlier head as the only blocker. In that case:
|
|
1207
|
+
|
|
1208
|
+
1. **Do not wait for the window to reset, and do not re-request.** Waiting
|
|
1209
|
+
leaves the pull request wedged for an hour or more. A re-request under the
|
|
1210
|
+
throttle settles hollow and can take away the bot's standing review (see
|
|
1211
|
+
above).
|
|
1212
|
+
2. Post the local adversarial review substitute for the **current** head, as
|
|
1213
|
+
step (d) case 3 describes, if one is not already there.
|
|
1214
|
+
3. **Then stop** with `blocked:stale-bot-verdict` (section 4). Report exactly
|
|
1215
|
+
that blocker: the bot reviewer, the review id, the outdated head it reviewed
|
|
1216
|
+
and the current head, the unresolved thread count, and that the substitute
|
|
1217
|
+
is posted for the current head. Name the decision that is needed: a human
|
|
1218
|
+
with dismissal rights dismisses that review, or decides it must stand.
|
|
1219
|
+
|
|
1220
|
+
Do not dismiss it on the strength of your own substitute. Here the bot's review
|
|
1221
|
+
is the only third-party review on the pull request. Dismissing it because of a
|
|
1222
|
+
review this run wrote would make the agent both author and judge of the change.
|
|
1223
|
+
That is the escalation the bullet above requires, reached right away instead of
|
|
1224
|
+
after a wait. A stranded verdict that is **not** the only substantive review, or
|
|
1225
|
+
one left by a reviewer that is not throttled, is still handled by the
|
|
1226
|
+
dismissal rule above.
|
|
1227
|
+
|
|
1175
1228
|
**Unverified, and stated as such rather than asserted:** that a bot's re-review
|
|
1176
1229
|
REPLACES its predecessor in the effective set is inferred from the observed
|
|
1177
1230
|
consequence, not measured against the vendor. It has not been tested here, and
|
|
@@ -1455,7 +1508,7 @@ Loop until one of:
|
|
|
1455
1508
|
the mode forbids acting on them: `on_blocker=report`). The latch is
|
|
1456
1509
|
deliberately not armed, so the PR sits OPEN rather than merging on a review
|
|
1457
1510
|
nothing performed. Report the PR URL, the review context's settled
|
|
1458
|
-
description, whether it
|
|
1511
|
+
description, whether it clears on its own (a rate limit) or is standing, and which sole-gate
|
|
1459
1512
|
condition failed. This is an outcome that needs an operator, stated in one
|
|
1460
1513
|
line a non-technical reader can act on — for example: *"PR #123 is ready to
|
|
1461
1514
|
merge but nobody reviewed it: the review tool says it skips public
|
|
@@ -1485,6 +1538,16 @@ Loop until one of:
|
|
|
1485
1538
|
— but a review asked for changes and that is still on record, so it will sit
|
|
1486
1539
|
there forever. Someone needs to clear that review or resolve the open
|
|
1487
1540
|
comments."* Never report this PR as merging.
|
|
1541
|
+
- **`blocked:stale-bot-verdict`** — the only thing blocking the merge is a
|
|
1542
|
+
bot's `CHANGES_REQUESTED` on an outdated head, the bot is rate limited or out
|
|
1543
|
+
of review allowance so it cannot replace that verdict, and the local
|
|
1544
|
+
adversarial review substitute is posted for the current head (step (e)).
|
|
1545
|
+
Report the reviewer, the review id, both heads, the unresolved thread count,
|
|
1546
|
+
and the dismissal decision a human needs to make. For example: *"PR #123 is
|
|
1547
|
+
ready except for an old review from the review bot on an earlier version. The
|
|
1548
|
+
bot is rate limited and cannot re-review, and I posted my own review of the
|
|
1549
|
+
current version. Someone with dismissal rights needs to dismiss review 456,
|
|
1550
|
+
or decide it should stand."* Do not wait for the bot's window to reset.
|
|
1488
1551
|
- **`CLOSED`** → report (PR was closed without merge).
|
|
1489
1552
|
- **Hard block needing a human**: an unresolvable conflict, a failing check that
|
|
1490
1553
|
needs design input, or genuine unresolved human objection (not a bot gate). Stop
|
|
@@ -1593,7 +1656,8 @@ declare none, one, or several, and each shows its own evidence.
|
|
|
1593
1656
|
|
|
1594
1657
|
Quote the description that was actually read. A report that says "rate limited"
|
|
1595
1658
|
on a policy skip is a wrong record, and the two call for different operator
|
|
1596
|
-
actions: one clears itself, the other never will.
|
|
1659
|
+
actions: one clears itself, the other never will. Neither changes what this
|
|
1660
|
+
skill does in the meantime: it substitutes and keeps driving in both cases.
|
|
1597
1661
|
|
|
1598
1662
|
`NOT REVIEWED` is reporting, and on a merged PR it is never a terminal state of
|
|
1599
1663
|
its own: it does not turn a merged PR into a blocked one. What it does gate is
|
|
@@ -75,6 +75,14 @@ node scripts/check-skipped-required-checks.mjs --pr=<pr> --json
|
|
|
75
75
|
It reports and never fails — a hollow check is often a vendor spending cap, not
|
|
76
76
|
a repository defect, and it is not this skill's call to block on one.
|
|
77
77
|
|
|
78
|
+
**Do not wait for a rate limit to reset, and do not re-request the review.** A
|
|
79
|
+
`Review rate limited` status, including decorated forms such as
|
|
80
|
+
`Review rate limited (retry in 12m)`, is handled like every other vacuous
|
|
81
|
+
review. Report it here. `lisa-drive-pr-to-merge` then posts a local adversarial
|
|
82
|
+
review substitute for the current head right away and keeps driving. Waiting
|
|
83
|
+
out the window stalls the pull request for an hour or more and reads no code.
|
|
84
|
+
Re-requesting overwrites the status and can destroy a real review.
|
|
85
|
+
|
|
78
86
|
Carry the finding into your Step 4 report. **Do not write "no unresolved review
|
|
79
87
|
threads" on its own** — it is true of an unreviewed PR too. Say which you
|
|
80
88
|
observed:
|
|
@@ -275,7 +275,7 @@ No create, no update, no rotate. Writing secrets or their notes requires an auth
|
|
|
275
275
|
|
|
276
276
|
The two writers are siblings, not modes: `rotate-secret.mjs` replaces a value **at its source**, and `sync-secret-to-ci.mjs` copies one **into a second store** without touching the source. Each needs an authority the resolver must not hold, so each is its own program with its own declaration list.
|
|
277
277
|
|
|
278
|
-
A third, narrow writer lives outside this skill: the session manager's `store-token` (`lisa-session-manager/scripts/token-store.mjs`) stores the Telegram bot token as `TELEGRAM_BOT_TOKEN_<NAME>` through `providers.mjs` `createSecret` (a new record) or `writeSecret` (`--replace`), then reads it back through the ordinary provider view and compares hashes before deleting the plaintext file it replaces. It writes that one name only, and only when an operator runs it. `createSecret` is Bitwarden-only; on any other provider the operator creates the secret and `store-token` verifies it. Like `bws secret edit`, `bws secret create` (2.1.0) takes the value as a positional argument and its help offers no stdin or file option, so the value is in argv for the life of that one call: the
|
|
278
|
+
A third, narrow writer lives outside this skill: the session manager's `store-token` (`lisa-session-manager/scripts/token-store.mjs`) stores the Telegram bot token as `TELEGRAM_BOT_TOKEN_<NAME>` through `providers.mjs` `createSecret` (a new record) or `writeSecret` (`--replace`), then reads it back through the ordinary provider view and compares hashes before deleting the plaintext file it replaces. It writes that one name only, and only when an operator runs it. `createSecret` is Bitwarden-only; on any other provider the operator creates the secret and `store-token` verifies it. Like `bws secret edit`, `bws secret create` (2.1.0) takes the value as a positional argument and its help offers no stdin or file option, so the value is in argv for the life of that one call: the second documented exception in Rule 4, held to the same limits (no shell, the CLI's output never shown, here through `--output none`, and a failure message that never repeats the command line).
|
|
279
279
|
|
|
280
280
|
## Rotating credentials
|
|
281
281
|
|
|
@@ -429,6 +429,6 @@ Cache **in-process only**. Never write a resolved value to disk except through t
|
|
|
429
429
|
1. **Never read a keychain, `.env`, or provider CLI outside this skill.** One chokepoint is what makes the single-store rule enforceable.
|
|
430
430
|
2. **Never log a secret value.** Print a length or a hash prefix when proving identity.
|
|
431
431
|
3. **Never write a resolved value to disk** except through the materialization contract, on a surface whose capabilities permit it.
|
|
432
|
-
4. **Never pass a secret as a command-line argument.**
|
|
432
|
+
4. **Never pass a secret as a command-line argument.** There are two documented exceptions, both Bitwarden writes whose CLI (2.1.0) takes the value only on the command line: `bws secret edit --value` (the rotation write, `writeSecret`) and `bws secret create` (`store-token`'s `createSecret`). Each is confined to that single operation and noted in the code, and both are held to the same limits: no shell, the CLI's output (which echoes the record) is never shown (`create` passes `--output none`; `edit`'s output is captured and discarded), and a failure message never repeats the command line.
|
|
433
433
|
5. **Verify a credential when it is stored, not when it is first used.** An unverified credential is indistinguishable from a broken one, and the gap between the two is measured in weeks.
|
|
434
434
|
6. **Treat a mismatch as stop-and-ask.** If a value differs between two places, they are two live credentials — not a stale copy to be discarded. Deleting the one you cannot verify leaves a working credential that no record accounts for.
|
|
@@ -170,8 +170,8 @@ Per cycle
|
|
|
170
170
|
[--sender <name>] STDIN: question
|
|
171
171
|
--kind: worker-question (default) | repo-route | exhausted | decision.
|
|
172
172
|
No message id with no channel. A re-ask keeps the item's marker
|
|
173
|
-
fields (markerType, marker
|
|
174
|
-
re-ping of the same text.
|
|
173
|
+
fields (markerType, marker); markedAt and a queued answer survive only
|
|
174
|
+
a re-ping of the same text, so a changed question must be marked again.
|
|
175
175
|
ledger marked <ref> --marker-type label|role --marker <value>
|
|
176
176
|
--comment-id <id>
|
|
177
177
|
Records that the human-needed marker was applied on the tracker and
|
|
@@ -177,9 +177,14 @@ export function listPendingQuestions(ledger, answerCommand) {
|
|
|
177
177
|
* gets a placeholder item with zero attempts.
|
|
178
178
|
*
|
|
179
179
|
* A re-ask keeps how the item is marked on the tracker (`markerType`,
|
|
180
|
-
* `marker
|
|
181
|
-
*
|
|
182
|
-
*
|
|
180
|
+
* `marker`), so re-sending a question never makes the manager forget the
|
|
181
|
+
* marker it must remove. `markedAt` survives only a re-ping of the same text:
|
|
182
|
+
* a changed question has not been posted or read back yet, so it reads as
|
|
183
|
+
* unmarked until `ledger marked` records the new question comment. Keeping it
|
|
184
|
+
* would let the next cycle run `item-answer` against the old question comment
|
|
185
|
+
* and queue a reply to the old question as the answer to the new one
|
|
186
|
+
* (CodySwannGT/lisa#4570). A queued answer survives only a re-ping of the same
|
|
187
|
+
* text for the same reason: it answered that text.
|
|
183
188
|
* `kind` defaults to the previous kind, then `worker-question`.
|
|
184
189
|
* @param {string} projectRoot - Absolute project root
|
|
185
190
|
* @param {{ref: string, text: string, telegramMessageId: number|null,
|
|
@@ -213,7 +218,7 @@ export function setPendingQuestion(
|
|
|
213
218
|
telegramMessageId: telegramMessageId ?? null,
|
|
214
219
|
markerType: existing?.markerType ?? null,
|
|
215
220
|
marker: existing?.marker ?? null,
|
|
216
|
-
markedAt: existing?.markedAt ?? null,
|
|
221
|
+
markedAt: sameText ? (existing?.markedAt ?? null) : null,
|
|
217
222
|
managerCommentIds: existing?.managerCommentIds ?? [],
|
|
218
223
|
answer: sameText ? (existing.answer ?? null) : null,
|
|
219
224
|
},
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
# for hours before someone queried the field by hand.
|
|
17
17
|
#
|
|
18
18
|
# The stale case is the one actually observed, and it is the one prose cannot
|
|
19
|
-
# help with:
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
19
|
+
# help with: a CHANGES_REQUESTED review stays attached to the commit it was made
|
|
20
|
+
# on and pushing the fix never clears it, while
|
|
21
|
+
# `required_approving_review_count: 0` means no scheduled re-review will ever
|
|
22
|
+
# clear it either. The PR is blocked indefinitely and every check-based signal
|
|
23
|
+
# reads ready. `dismiss_stale_reviews_on_push` does not help: GitHub dismisses
|
|
24
|
+
# stale APPROVALS only, so a change request survives a push under either
|
|
25
|
+
# setting (measured 2026-11-01, CodySwannGT/lisa#4574).
|
|
24
26
|
#
|
|
25
27
|
# WHAT IT CHECKS, AND WHY THAT AND NOT THE CHECK ROLLUP
|
|
26
28
|
#
|
|
@@ -1048,9 +1050,9 @@ REFUSAL = """Blocked: arming auto-merge on %s would report a merge that can neve
|
|
|
1048
1050
|
|
|
1049
1051
|
GitHub reports reviewDecision = CHANGES_REQUESTED. That is a merge blocker with
|
|
1050
1052
|
NO check-run representation: the failing-check count reads 0 and the checks tab
|
|
1051
|
-
is entirely green while the PR is permanently unmergeable.
|
|
1052
|
-
|
|
1053
|
-
no re-review is scheduled to.
|
|
1053
|
+
is entirely green while the PR is permanently unmergeable. Pushing the fix does
|
|
1054
|
+
not clear it (dismiss_stale_reviews_on_push dismisses stale approvals only, not
|
|
1055
|
+
change requests), and no re-review is scheduled to.
|
|
1054
1056
|
|
|
1055
1057
|
Clear the blocker, then arm:
|
|
1056
1058
|
- resolve every review thread and address or reply to the feedback
|
|
@@ -391,15 +391,16 @@ declared, arm normally (the scope rule above). Where one IS declared, absent a
|
|
|
391
391
|
description that positively states a review ran, treat the context as vacuous.
|
|
392
392
|
Fail closed: absence of evidence is not evidence of a review.
|
|
393
393
|
|
|
394
|
-
#### Vacuous
|
|
394
|
+
#### Vacuous, transient or standing: the response is the same
|
|
395
395
|
|
|
396
|
-
A vacuous context is
|
|
397
|
-
|
|
398
|
-
|
|
396
|
+
A vacuous context is never something to wait out. Waiting looks like progress
|
|
397
|
+
but it is a stall, and on a repository where the state never clears it waits
|
|
398
|
+
forever. Some vacuities clear on their own and some do not. **That changes what
|
|
399
|
+
the report says, not what you do:**
|
|
399
400
|
|
|
400
401
|
| description | vacuous? | clears on its own? |
|
|
401
402
|
| --- | --- | --- |
|
|
402
|
-
| `Review rate limited` | yes |
|
|
403
|
+
| `Review rate limited` (and decorated forms such as `Review rate limited (retry in 12m)`) | yes | eventually — a throughput window of unknown length |
|
|
403
404
|
| `Review skipped: manual review required for this OSS repository` | yes | **no** — a standing vendor policy for public repositories |
|
|
404
405
|
| `Review skipped: N files exceed the limit of M` | yes | **no** — not without splitting the pull request |
|
|
405
406
|
| `Review skipped` (bare, no stated reason) | yes | **unknown — treat as standing** |
|
|
@@ -408,17 +409,33 @@ A bare skip carries no machine-readable reason, so nothing distinguishes it from
|
|
|
408
409
|
the standing forms. Requiring a stated reason before treating a skip as vacuous
|
|
409
410
|
is how the commonest form gets missed.
|
|
410
411
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
412
|
+
**Every row, the rate limit included: do not wait, and do not re-request.** Do
|
|
413
|
+
not arm. Go straight to the watch loop, run the local adversarial review over the
|
|
414
|
+
PR diff at the current head **now**, post it with the substitute marker (step
|
|
415
|
+
(d), "In case 3, substitute"), and keep driving. Fix what it finds, substitute
|
|
416
|
+
again on each new head, and let step (d) merge when the vacuous context is the
|
|
417
|
+
sole remaining gate.
|
|
418
|
+
|
|
419
|
+
The rate limit used to get a wait-the-window-out-and-re-request-once exception.
|
|
420
|
+
That exception is removed, for three reasons:
|
|
421
|
+
|
|
422
|
+
- **The window is not short.** Agents waiting it out stalled for an hour or more
|
|
423
|
+
per pull request, across a whole portfolio of repositories, and nothing about
|
|
424
|
+
the wait made the merge safer than a substitute review would.
|
|
425
|
+
- **Re-requesting does not work under a throttle.** Measured on #3220, a single
|
|
426
|
+
explicit re-request was acknowledged, went `Review in progress`, and settled
|
|
427
|
+
back at `Review rate limited` two seconds later having reviewed nothing. That
|
|
428
|
+
minted a second hollow green.
|
|
429
|
+
- **Re-requesting destroys evidence.** It overwrites the existing status (see
|
|
430
|
+
"Never re-request a review to 'refresh' it" in step (d)), so the exception
|
|
431
|
+
contradicted a rule this file states with no exceptions.
|
|
432
|
+
|
|
433
|
+
If the reviewer later reviews on its own, nothing special happens. Its threads
|
|
434
|
+
are review threads like any other, and step (d) handles them. A `CHANGES_REQUESTED`
|
|
435
|
+
it leaves is a real blocker (step (e)), and the substitute never overrides it.
|
|
436
|
+
If it now proves work, arm, per the table above. That is a re-classification
|
|
437
|
+
the watch loop makes on what it reads anyway. It is never a reason to wait or
|
|
438
|
+
re-request.
|
|
422
439
|
|
|
423
440
|
#### A latch somebody else armed
|
|
424
441
|
|
|
@@ -642,17 +659,24 @@ step (e) first — which discriminates a live objection from a stranded one by
|
|
|
642
659
|
unresolved thread count, not by `reviewDecision` — and arm afterwards.
|
|
643
660
|
|
|
644
661
|
**Two ruleset settings decide whether a standing verdict ever clears itself, and
|
|
645
|
-
|
|
646
|
-
than assuming, with `gh api repos/<owner>/<repo>/
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
662
|
+
neither one clears a change request.** Verify them for the repo you are in
|
|
663
|
+
rather than assuming, with `gh api repos/<owner>/<repo>/rules/branches/<base>`
|
|
664
|
+
(the rules in force on the base branch, with their parameters; the
|
|
665
|
+
`gh api repos/<owner>/<repo>/rulesets` list carries no rule parameters):
|
|
666
|
+
|
|
667
|
+
- `dismiss_stale_reviews_on_push` dismisses stale **approvals** only. The `base`
|
|
668
|
+
ruleset Lisa generates defaults it to `true` when
|
|
669
|
+
`policy.protect.dismiss_stale_reviews` is undeclared, and keeps a declared
|
|
670
|
+
value (CodySwannGT/lisa#4574), but a
|
|
671
|
+
`CHANGES_REQUESTED` review made on an older head stays in force after a push
|
|
672
|
+
under either setting (measured on a probe repository on 2026-11-01: the push
|
|
673
|
+
changed the diff and `reviewDecision` stayed `CHANGES_REQUESTED`), so
|
|
674
|
+
**pushing the fix never clears it**.
|
|
651
675
|
- `required_approving_review_count: 0` — no approval is required to merge, so a
|
|
652
676
|
standing `CHANGES_REQUESTED` is not holding a slot for a review that would
|
|
653
677
|
otherwise arrive. Nothing is scheduled to clear it.
|
|
654
678
|
|
|
655
|
-
|
|
679
|
+
Where both hold, the block is **indefinite, not slow**. Waiting is not a
|
|
656
680
|
strategy; something has to act.
|
|
657
681
|
|
|
658
682
|
**Unresolved threads block independently of the verdict.** This repository sets
|
|
@@ -1046,10 +1070,11 @@ configured reviewed-when phrase says what "did work" looks like for this
|
|
|
1046
1070
|
project.
|
|
1047
1071
|
|
|
1048
1072
|
Prove the signal from the commit status API as above; a merely pending or queued
|
|
1049
|
-
check is not proof of anything and must keep polling. **A
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1073
|
+
check is not proof of anything and must keep polling. **A settled vacuity, the
|
|
1074
|
+
rate limit included, arrives here immediately.** There is no wait to spend first
|
|
1075
|
+
and no re-request to try: a rate limit's window is of unknown length, and
|
|
1076
|
+
waiting it out stalls the pull request without reading a line of the diff. The
|
|
1077
|
+
substitute is what makes the merge safe, not the passage of time.
|
|
1053
1078
|
|
|
1054
1079
|
Once the signal is explicit, merge directly with `gh pr merge <pr>
|
|
1055
1080
|
--<merge_method>` (pass `--admin` only if branch protection lists the vacuous
|
|
@@ -1170,6 +1195,34 @@ Deleting the fallback outright would over-correct: it is the only remedy a lane
|
|
|
1170
1195
|
without dismissal permission has, and that permission is not universal. This
|
|
1171
1196
|
narrows it to the case that costs nothing.
|
|
1172
1197
|
|
|
1198
|
+
**A stale bot verdict under a rate limit: substitute, then stop and report.**
|
|
1199
|
+
Under a rate limit or an exhausted review allowance, a bot's old
|
|
1200
|
+
`CHANGES_REQUESTED` on an outdated head can end up as the only thing blocking
|
|
1201
|
+
the merge. The bot cannot post a fresh review to replace it, so the verdict
|
|
1202
|
+
will not clear by itself while the throttle lasts. This was measured on a
|
|
1203
|
+
caller repo in the portfolio: every check green, zero unresolved threads, and
|
|
1204
|
+
the bot's verdict from an earlier head as the only blocker. In that case:
|
|
1205
|
+
|
|
1206
|
+
1. **Do not wait for the window to reset, and do not re-request.** Waiting
|
|
1207
|
+
leaves the pull request wedged for an hour or more. A re-request under the
|
|
1208
|
+
throttle settles hollow and can take away the bot's standing review (see
|
|
1209
|
+
above).
|
|
1210
|
+
2. Post the local adversarial review substitute for the **current** head, as
|
|
1211
|
+
step (d) case 3 describes, if one is not already there.
|
|
1212
|
+
3. **Then stop** with `blocked:stale-bot-verdict` (section 4). Report exactly
|
|
1213
|
+
that blocker: the bot reviewer, the review id, the outdated head it reviewed
|
|
1214
|
+
and the current head, the unresolved thread count, and that the substitute
|
|
1215
|
+
is posted for the current head. Name the decision that is needed: a human
|
|
1216
|
+
with dismissal rights dismisses that review, or decides it must stand.
|
|
1217
|
+
|
|
1218
|
+
Do not dismiss it on the strength of your own substitute. Here the bot's review
|
|
1219
|
+
is the only third-party review on the pull request. Dismissing it because of a
|
|
1220
|
+
review this run wrote would make the agent both author and judge of the change.
|
|
1221
|
+
That is the escalation the bullet above requires, reached right away instead of
|
|
1222
|
+
after a wait. A stranded verdict that is **not** the only substantive review, or
|
|
1223
|
+
one left by a reviewer that is not throttled, is still handled by the
|
|
1224
|
+
dismissal rule above.
|
|
1225
|
+
|
|
1173
1226
|
**Unverified, and stated as such rather than asserted:** that a bot's re-review
|
|
1174
1227
|
REPLACES its predecessor in the effective set is inferred from the observed
|
|
1175
1228
|
consequence, not measured against the vendor. It has not been tested here, and
|
|
@@ -1453,7 +1506,7 @@ Loop until one of:
|
|
|
1453
1506
|
the mode forbids acting on them: `on_blocker=report`). The latch is
|
|
1454
1507
|
deliberately not armed, so the PR sits OPEN rather than merging on a review
|
|
1455
1508
|
nothing performed. Report the PR URL, the review context's settled
|
|
1456
|
-
description, whether it
|
|
1509
|
+
description, whether it clears on its own (a rate limit) or is standing, and which sole-gate
|
|
1457
1510
|
condition failed. This is an outcome that needs an operator, stated in one
|
|
1458
1511
|
line a non-technical reader can act on — for example: *"PR #123 is ready to
|
|
1459
1512
|
merge but nobody reviewed it: the review tool says it skips public
|
|
@@ -1483,6 +1536,16 @@ Loop until one of:
|
|
|
1483
1536
|
— but a review asked for changes and that is still on record, so it will sit
|
|
1484
1537
|
there forever. Someone needs to clear that review or resolve the open
|
|
1485
1538
|
comments."* Never report this PR as merging.
|
|
1539
|
+
- **`blocked:stale-bot-verdict`** — the only thing blocking the merge is a
|
|
1540
|
+
bot's `CHANGES_REQUESTED` on an outdated head, the bot is rate limited or out
|
|
1541
|
+
of review allowance so it cannot replace that verdict, and the local
|
|
1542
|
+
adversarial review substitute is posted for the current head (step (e)).
|
|
1543
|
+
Report the reviewer, the review id, both heads, the unresolved thread count,
|
|
1544
|
+
and the dismissal decision a human needs to make. For example: *"PR #123 is
|
|
1545
|
+
ready except for an old review from the review bot on an earlier version. The
|
|
1546
|
+
bot is rate limited and cannot re-review, and I posted my own review of the
|
|
1547
|
+
current version. Someone with dismissal rights needs to dismiss review 456,
|
|
1548
|
+
or decide it should stand."* Do not wait for the bot's window to reset.
|
|
1486
1549
|
- **`CLOSED`** → report (PR was closed without merge).
|
|
1487
1550
|
- **Hard block needing a human**: an unresolvable conflict, a failing check that
|
|
1488
1551
|
needs design input, or genuine unresolved human objection (not a bot gate). Stop
|
|
@@ -1591,7 +1654,8 @@ declare none, one, or several, and each shows its own evidence.
|
|
|
1591
1654
|
|
|
1592
1655
|
Quote the description that was actually read. A report that says "rate limited"
|
|
1593
1656
|
on a policy skip is a wrong record, and the two call for different operator
|
|
1594
|
-
actions: one clears itself, the other never will.
|
|
1657
|
+
actions: one clears itself, the other never will. Neither changes what this
|
|
1658
|
+
skill does in the meantime: it substitutes and keeps driving in both cases.
|
|
1595
1659
|
|
|
1596
1660
|
`NOT REVIEWED` is reporting, and on a merged PR it is never a terminal state of
|
|
1597
1661
|
its own: it does not turn a merged PR into a blocked one. What it does gate is
|
|
@@ -73,6 +73,14 @@ node scripts/check-skipped-required-checks.mjs --pr=<pr> --json
|
|
|
73
73
|
It reports and never fails — a hollow check is often a vendor spending cap, not
|
|
74
74
|
a repository defect, and it is not this skill's call to block on one.
|
|
75
75
|
|
|
76
|
+
**Do not wait for a rate limit to reset, and do not re-request the review.** A
|
|
77
|
+
`Review rate limited` status, including decorated forms such as
|
|
78
|
+
`Review rate limited (retry in 12m)`, is handled like every other vacuous
|
|
79
|
+
review. Report it here. `lisa-drive-pr-to-merge` then posts a local adversarial
|
|
80
|
+
review substitute for the current head right away and keeps driving. Waiting
|
|
81
|
+
out the window stalls the pull request for an hour or more and reads no code.
|
|
82
|
+
Re-requesting overwrites the status and can destroy a real review.
|
|
83
|
+
|
|
76
84
|
Carry the finding into your Step 4 report. **Do not write "no unresolved review
|
|
77
85
|
threads" on its own** — it is true of an unreviewed PR too. Say which you
|
|
78
86
|
observed:
|
|
@@ -273,7 +273,7 @@ No create, no update, no rotate. Writing secrets or their notes requires an auth
|
|
|
273
273
|
|
|
274
274
|
The two writers are siblings, not modes: `rotate-secret.mjs` replaces a value **at its source**, and `sync-secret-to-ci.mjs` copies one **into a second store** without touching the source. Each needs an authority the resolver must not hold, so each is its own program with its own declaration list.
|
|
275
275
|
|
|
276
|
-
A third, narrow writer lives outside this skill: the session manager's `store-token` (`lisa-session-manager/scripts/token-store.mjs`) stores the Telegram bot token as `TELEGRAM_BOT_TOKEN_<NAME>` through `providers.mjs` `createSecret` (a new record) or `writeSecret` (`--replace`), then reads it back through the ordinary provider view and compares hashes before deleting the plaintext file it replaces. It writes that one name only, and only when an operator runs it. `createSecret` is Bitwarden-only; on any other provider the operator creates the secret and `store-token` verifies it. Like `bws secret edit`, `bws secret create` (2.1.0) takes the value as a positional argument and its help offers no stdin or file option, so the value is in argv for the life of that one call: the
|
|
276
|
+
A third, narrow writer lives outside this skill: the session manager's `store-token` (`lisa-session-manager/scripts/token-store.mjs`) stores the Telegram bot token as `TELEGRAM_BOT_TOKEN_<NAME>` through `providers.mjs` `createSecret` (a new record) or `writeSecret` (`--replace`), then reads it back through the ordinary provider view and compares hashes before deleting the plaintext file it replaces. It writes that one name only, and only when an operator runs it. `createSecret` is Bitwarden-only; on any other provider the operator creates the secret and `store-token` verifies it. Like `bws secret edit`, `bws secret create` (2.1.0) takes the value as a positional argument and its help offers no stdin or file option, so the value is in argv for the life of that one call: the second documented exception in Rule 4, held to the same limits (no shell, the CLI's output never shown, here through `--output none`, and a failure message that never repeats the command line).
|
|
277
277
|
|
|
278
278
|
## Rotating credentials
|
|
279
279
|
|
|
@@ -427,6 +427,6 @@ Cache **in-process only**. Never write a resolved value to disk except through t
|
|
|
427
427
|
1. **Never read a keychain, `.env`, or provider CLI outside this skill.** One chokepoint is what makes the single-store rule enforceable.
|
|
428
428
|
2. **Never log a secret value.** Print a length or a hash prefix when proving identity.
|
|
429
429
|
3. **Never write a resolved value to disk** except through the materialization contract, on a surface whose capabilities permit it.
|
|
430
|
-
4. **Never pass a secret as a command-line argument.**
|
|
430
|
+
4. **Never pass a secret as a command-line argument.** There are two documented exceptions, both Bitwarden writes whose CLI (2.1.0) takes the value only on the command line: `bws secret edit --value` (the rotation write, `writeSecret`) and `bws secret create` (`store-token`'s `createSecret`). Each is confined to that single operation and noted in the code, and both are held to the same limits: no shell, the CLI's output (which echoes the record) is never shown (`create` passes `--output none`; `edit`'s output is captured and discarded), and a failure message never repeats the command line.
|
|
431
431
|
5. **Verify a credential when it is stored, not when it is first used.** An unverified credential is indistinguishable from a broken one, and the gap between the two is measured in weeks.
|
|
432
432
|
6. **Treat a mismatch as stop-and-ask.** If a value differs between two places, they are two live credentials — not a stale copy to be discarded. Deleting the one you cannot verify leaves a working credential that no record accounts for.
|
|
@@ -170,8 +170,8 @@ Per cycle
|
|
|
170
170
|
[--sender <name>] STDIN: question
|
|
171
171
|
--kind: worker-question (default) | repo-route | exhausted | decision.
|
|
172
172
|
No message id with no channel. A re-ask keeps the item's marker
|
|
173
|
-
fields (markerType, marker
|
|
174
|
-
re-ping of the same text.
|
|
173
|
+
fields (markerType, marker); markedAt and a queued answer survive only
|
|
174
|
+
a re-ping of the same text, so a changed question must be marked again.
|
|
175
175
|
ledger marked <ref> --marker-type label|role --marker <value>
|
|
176
176
|
--comment-id <id>
|
|
177
177
|
Records that the human-needed marker was applied on the tracker and
|
|
@@ -177,9 +177,14 @@ export function listPendingQuestions(ledger, answerCommand) {
|
|
|
177
177
|
* gets a placeholder item with zero attempts.
|
|
178
178
|
*
|
|
179
179
|
* A re-ask keeps how the item is marked on the tracker (`markerType`,
|
|
180
|
-
* `marker
|
|
181
|
-
*
|
|
182
|
-
*
|
|
180
|
+
* `marker`), so re-sending a question never makes the manager forget the
|
|
181
|
+
* marker it must remove. `markedAt` survives only a re-ping of the same text:
|
|
182
|
+
* a changed question has not been posted or read back yet, so it reads as
|
|
183
|
+
* unmarked until `ledger marked` records the new question comment. Keeping it
|
|
184
|
+
* would let the next cycle run `item-answer` against the old question comment
|
|
185
|
+
* and queue a reply to the old question as the answer to the new one
|
|
186
|
+
* (CodySwannGT/lisa#4570). A queued answer survives only a re-ping of the same
|
|
187
|
+
* text for the same reason: it answered that text.
|
|
183
188
|
* `kind` defaults to the previous kind, then `worker-question`.
|
|
184
189
|
* @param {string} projectRoot - Absolute project root
|
|
185
190
|
* @param {{ref: string, text: string, telegramMessageId: number|null,
|
|
@@ -213,7 +218,7 @@ export function setPendingQuestion(
|
|
|
213
218
|
telegramMessageId: telegramMessageId ?? null,
|
|
214
219
|
markerType: existing?.markerType ?? null,
|
|
215
220
|
marker: existing?.marker ?? null,
|
|
216
|
-
markedAt: existing?.markedAt ?? null,
|
|
221
|
+
markedAt: sameText ? (existing?.markedAt ?? null) : null,
|
|
217
222
|
managerCommentIds: existing?.managerCommentIds ?? [],
|
|
218
223
|
answer: sameText ? (existing.answer ?? null) : null,
|
|
219
224
|
},
|