@codyswann/lisa 3.41.1 → 3.43.0

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.
Files changed (66) hide show
  1. package/all/copy-overwrite/scripts/lisa-environment-prepare.mjs +356 -0
  2. package/all/copy-overwrite/scripts/lisa-work-item.mjs +203 -1
  3. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  4. package/dist/core/lisa-owned-hash-ledger.js +6 -0
  5. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +12 -3
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/expo/create-only/.github/workflows/playwright-e2e.yml +92 -75
  10. package/package.json +1 -1
  11. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
  14. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
  15. package/plugins/lisa-agy/plugin.json +1 -1
  16. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
  17. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  19. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  20. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
  24. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
  26. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo-agy/plugin.json +1 -1
  29. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  49. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails-agy/plugin.json +1 -1
  54. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  59. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  64. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +50 -0
@@ -5,20 +5,24 @@ name: 🎭 Playwright Web E2E
5
5
 
6
6
  # Standalone Playwright suite — the web counterpart to maestro-e2e.yml.
7
7
  #
8
- # WHY THIS EXISTS. Playwright used to ride inside ci.yml as the
9
- # `playwright_e2e` job of Lisa's reusable quality.yml. That coupled the
10
- # heaviest and flakiest suite in the project to the lint/typecheck/unit gate:
11
- # one workflow, one concurrency group, one cadence, and a red Playwright shard
12
- # blocking a pull request that only changed a type. Splitting it out gives it
13
- # its own cadence, its own concurrency, one target environment, and a
14
- # dedicated red/green signal a nightly gate can read.
8
+ # WHY THIS EXISTS. Playwright used to ride inside ci.yml as the `playwright_e2e`
9
+ # job of Lisa's reusable quality.yml. That coupled the heaviest and flakiest
10
+ # suite in the project to the lint/typecheck/unit gate: one workflow, one
11
+ # concurrency group, one cadence, and a red Playwright shard blocking a pull
12
+ # request that only changed a type.
15
13
  #
16
- # ── BEFORE THIS FILE DOES ANYTHING, DECLARE THE GATE ───────────────────────
14
+ # It now calls a DEDICATED reusable workflow. The previous shape of this file
15
+ # called quality.yml and passed `skip_jobs` naming the two dozen jobs it did not
16
+ # want — an inversion that went stale silently, because a job added to
17
+ # quality.yml was absent from this hand-maintained list and therefore RAN on a
18
+ # nightly whose whole point is that it runs one suite. That list is gone.
19
+ #
20
+ # ── DECLARING THE GATE ─────────────────────────────────────────────────────
17
21
  #
18
22
  # This workflow resolves the `e2e-browser` gate at the `continuous:development`
19
- # moment. Until `.lisa.config.json` declares it, quality.yml runs its BUILT-IN
20
- # Playwright path here (sharded `npx playwright test`), which is a reasonable
21
- # default but is not a decision anybody recorded. To make it one:
23
+ # moment. Until `.lisa.config.json` declares it, Lisa runs its BUILT-IN
24
+ # Playwright path (sharded `npx playwright test`), which is a reasonable default
25
+ # but is not a decision anybody recorded. To make it one:
22
26
  #
23
27
  # {
24
28
  # "gates": {
@@ -27,29 +31,21 @@ name: 🎭 Playwright Web E2E
27
31
  # }
28
32
  # }
29
33
  #
30
- # `runner` is optional and defaults to `<package_manager> run`. The gate's task
31
- # is `test:e2e`, so the project needs that script in package.json — Lisa
32
- # resolves the PROPERTY ("browser journeys pass end to end"), never the tool.
34
+ # The gate's task is `test:e2e`, so the project needs that script in
35
+ # package.json Lisa resolves the PROPERTY ("browser journeys pass end to
36
+ # end"), never the tool.
33
37
  #
34
38
  # TWO THINGS THAT ARE NOT INTERCHANGEABLE:
35
39
  #
36
40
  # * OMITTING the declaration and declaring it `off` are different. `off` is a
37
41
  # recorded decision and drops the required context. Omitting it leaves
38
42
  # Lisa's built-in path running. Neither is "no gate".
39
- # * DECLARING IT WITH NO SUITE is refused, not greened. quality.yml fails the
40
- # job when `e2e-browser` resolves and there is no playwright.config.ts,
41
- # because a browser gate over an absent suite reports green having run
42
- # nothing and is indistinguishable on the pull-request page from one that
43
- # ran. If this project has no Playwright specs, DELETE THIS FILE rather
44
- # than declaring the gate against it.
45
- #
46
- # ── SHARDING ───────────────────────────────────────────────────────────────
47
- #
48
- # `playwright_shards` splits Lisa's BUILT-IN invocation across N runners. It
49
- # does not apply once `e2e-browser` resolves to a task: Lisa cannot append
50
- # `--shard` to a command whose shape it does not know, and the `--` passthrough
51
- # differs between npm, yarn and bun. A declared gate runs on one runner. Shard
52
- # inside the project's own `test:e2e` script if the suite needs it.
43
+ # * DECLARING IT WITH NO SUITE is refused, not greened. The job fails when
44
+ # `e2e-browser` resolves and there is no playwright.config.ts, because a
45
+ # browser gate over an absent suite reports green having run nothing and is
46
+ # indistinguishable on the pull-request page from one that ran. If this
47
+ # project has no Playwright specs, DELETE THIS FILE rather than declaring
48
+ # the gate against it.
53
49
 
54
50
  on:
55
51
  schedule:
@@ -61,14 +57,40 @@ on:
61
57
  - cron: '0 7 * * *'
62
58
  workflow_dispatch:
63
59
 
64
- # Deliberately no `pull_request` trigger. This suite is too slow to gate a
65
- # pull request, and adding one here would also collide with ci.yml: two
66
- # workflows must not report the same check name, and the identity is the
67
- # JOB name, not the workflow's. A reusable call reports as
68
- # `<caller job name> / <callee job name>`, so the job below reports as
69
- # `🎭 Playwright Web E2E / 🎭 Playwright E2E Tests`. Keep ci.yml's
70
- # `skip_jobs` naming `playwright_e2e` so this workflow is the only place the
71
- # suite runs.
60
+ # Deliberately no `pull_request` trigger. This suite is too slow to gate a pull
61
+ # request, and adding one here would also collide with ci.yml: two workflows
62
+ # must not report the same check name, and the identity is the JOB name, not the
63
+ # workflow's.
64
+ #
65
+ # ── THE COLLISION IS STILL LIVE, AND THIS NOTE IS THE ONLY DEFENCE ─────────
66
+ #
67
+ # quality.yml STILL CONTAINS its own copy of the Playwright jobs. Removing them
68
+ # there is a breaking change for every already-installed caller tracking `@main`,
69
+ # so it happens only once consumers have migrated to the dedicated workflow this
70
+ # file now calls. Until that removal lands, a project that runs quality.yml
71
+ # WITHOUT suppressing the suite runs it in two places and reports the same check
72
+ # name from both.
73
+ #
74
+ # SUPPRESS IT WITH THE GATE, NOT WITH `skip_jobs`. `skip_jobs` is being retired
75
+ # in favour of gate levels, and the gate already controls this suite completely
76
+ # inside quality.yml — `off` reaches the "e2e-browser declared off" path and
77
+ # neither the built-in run nor the declared task executes. So declare it off at
78
+ # the moment ci.yml runs at:
79
+ #
80
+ # {
81
+ # "gates": {
82
+ # "e2e-browser": {
83
+ # "pull-request": "off",
84
+ # "continuous:development": "required"
85
+ # }
86
+ # }
87
+ # }
88
+ #
89
+ # One declaration, both cadences, and each is a recorded decision rather than an
90
+ # inferred one. Declaring `off` also drops the required context, which is the
91
+ # visible way to stop requiring a check — as opposed to a job that silently
92
+ # stops running. Nothing inside this file can enforce it; the only defence is
93
+ # that whoever seeds it knows.
72
94
 
73
95
  permissions:
74
96
  contents: read
@@ -93,49 +115,44 @@ concurrency:
93
115
  jobs:
94
116
  playwright:
95
117
  name: 🎭 Playwright Web E2E
96
- uses: CodySwannGT/lisa/.github/workflows/quality.yml@main
118
+ uses: CodySwannGT/lisa/.github/workflows/playwright-e2e.yml@main
97
119
  with:
98
120
  node_version: '22.21.1'
99
121
  package_manager: 'bun'
100
- # The moment this workflow's cadence corresponds to. It is what makes
101
- # the `.lisa.config.json` declaration above readable from here — a gate
102
- # declared at `pull-request` is invisible at this moment, and vice
103
- # versa, so a nightly suite and a per-change suite cannot be turned on
104
- # by the same line of config by accident.
122
+ # The moment this workflow's cadence corresponds to. It is what makes the
123
+ # `.lisa.config.json` declaration above readable from here — a gate
124
+ # declared at `pull-request` is invisible at this moment, and vice versa,
125
+ # so a nightly suite and a per-change suite cannot be turned on by the
126
+ # same line of config by accident.
105
127
  moment: 'continuous:development'
106
- # Everything except `playwright_e2e`.
107
- #
108
- # THIS INVERSION IS A KNOWN-BAD SHAPE, carried because it is currently
109
- # the only one that works. quality.yml gates most jobs on `skip_jobs`
110
- # rather than on the moment, so a single-suite caller has to name the
111
- # two dozen jobs it does NOT want. It is exact-token matched against
112
- # `,{0},` — comma-delimited with NO SPACES, so `lint, lint_slow`
113
- # silently skips nothing beyond `lint`.
114
- #
115
- # IT GOES STALE SILENTLY, and that is the cost. A job added to
116
- # quality.yml is absent from this list and therefore RUNS here, on a
117
- # nightly whose whole point is that it runs one suite. Measured while
118
- # writing this file: the hand-maintained list in the first project to
119
- # adopt this shape was already missing five keys that exist today
120
- # (`bdd_coverage`, `state_classification`, `environment_reset`,
121
- # `environment_reseed`, `skipped_required_checks`). Re-derive it from
122
- # quality.yml when adding a suite, do not extend it from memory.
123
- skip_jobs: 'bdd_coverage,build,dead_code,e2e_coverage,environment_reseed,environment_reset,floor_collisions,format,learnings_budget,license_compliance,lint,lint_slow,maestro_e2e,npm_security_scan,secret_scanning,sg_scan,skipped_required_checks,snyk,sonarcloud,state_classification,test:e2e,test:integration,test:mutation,test:unit,test_node_suites,threshold_ratchet,typecheck,work_item_traceability,zap_baseline'
124
- # Cross-run mutex on whatever this suite shares with the OTHER suites
125
- # that touch the same backend. Deliberately DISTINCT from this
126
- # workflow's own top-level `concurrency` group above — a shared group
127
- # between a parent run and the child run it is waiting on deadlocks.
128
- # Join maestro-e2e.yml to the same group if the two suites share test
129
- # accounts or fixtures.
128
+ # Cross-run mutex on whatever this suite shares with the OTHER suites that
129
+ # touch the same backend. Deliberately DISTINCT from this workflow's own
130
+ # top-level `concurrency` group above a shared group between a parent
131
+ # run and the child run it is waiting on deadlocks. Join maestro-e2e.yml
132
+ # to the same group if the two suites share test accounts or fixtures.
130
133
  concurrency_group: 'e2e-shared-development'
131
- # Applies only while `e2e-browser` is undeclared see the SHARDING note
132
- # at the top of this file.
134
+ # Return the environment to a known state BEFORE the suite, rather than
135
+ # cleaning up after it. Cleanup-after is best-effort: it does not run when
136
+ # a runner dies, is cancelled, or is evicted, so the next night starts
137
+ # against whatever the last failure left behind.
138
+ #
139
+ # The named verbs must exist as `environment:reset` / `environment:reseed`
140
+ # scripts. A verb named here that the project does not declare FAILS the
141
+ # run rather than skipping — a suite against an environment nobody
142
+ # prepared reports green while proving nothing. Clear this input to opt
143
+ # out; that is a visible decision, which a silent skip is not.
144
+ prepare_environment: 'development'
145
+ prepare_verbs: 'reset,reseed'
146
+ # Applies only while `e2e-browser` is undeclared: Lisa cannot append
147
+ # `--shard` to a command whose shape it does not know, and the `--`
148
+ # passthrough differs between npm, yarn and bun. A declared gate runs on
149
+ # one runner; shard inside the project's own `test:e2e` script instead.
133
150
  playwright_shards: 2
134
- # Leave false unless the cache key has been checked against this
135
- # project's layout. Lisa's key is a fingerprint of build inputs, and a
136
- # key that misses the project's source directories freezes: `expo export`
137
- # then serves stale transforms and every source change silently tests old
138
- # code. A green suite over the previous commit is the worst failure this
151
+ # Leave false unless the cache key has been checked against this project's
152
+ # layout. Lisa's key is a fingerprint of build inputs, and a key that
153
+ # misses the project's source directories freezes: `expo export` then
154
+ # serves stale transforms and every source change silently tests old code.
155
+ # A green suite over the previous commit is the worst failure this
139
156
  # workflow has.
140
157
  cache_build: false
141
158
  # Runs before the web export, in the job that runs the suite. Bake the
@@ -145,7 +162,7 @@ jobs:
145
162
  #
146
163
  # Use `printf '\n%s\n'` rather than `echo >>`: the committed .env.<mode>
147
164
  # files do not end in a trailing newline, so a bare append concatenates
148
- # onto the last variable's value — corrupting it AND silently dropping
149
- # the line being added.
165
+ # onto the last variable's value — corrupting it AND silently dropping the
166
+ # line being added.
150
167
  playwright_setup_command: |
151
168
  cp .env.development .env.local
package/package.json CHANGED
@@ -132,7 +132,7 @@
132
132
  "ws": ">=8.21.0"
133
133
  },
134
134
  "name": "@codyswann/lisa",
135
- "version": "3.41.1",
135
+ "version": "3.43.0",
136
136
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
137
137
  "main": "dist/index.js",
138
138
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -470,6 +470,56 @@ matching Phase 4b's safe default. The full procedure is `lisa-linear-sync`
470
470
  Phase 4b; when the caller's flow already runs a `pr-merged` sync for this
471
471
  merge, confirming that sync ran satisfies this step.
472
472
 
473
+ ### d. Complete the work item
474
+
475
+ Run this once the PR reports `MERGED` and the ancestry check has passed:
476
+
477
+ ```
478
+ node scripts/lisa-work-item.mjs complete --ref <work-item>
479
+ ```
480
+
481
+ resolving the script the usual three ways (installed package, host `scripts/`,
482
+ this repo's own tree).
483
+
484
+ **This step exists because the previous arrangement did not work, measured.**
485
+ The instruction to move a work item to its terminal role lived in
486
+ `lisa-git-submit-pr`, in a section reached *after* that skill delegates the
487
+ entire merge loop to this one. So the skill that observes the merge was never
488
+ told to close anything, and the skill that was told had already handed off. The
489
+ result: **27 of 27** open items carrying the claimed role in this repository had
490
+ a merged pull request — the claimed lane reported 27 things in flight when the
491
+ real number was one.
492
+
493
+ Three properties make the command safe to run unconditionally:
494
+
495
+ 1. **The terminal role is RESOLVED, never assumed.** `lifecycleContract` reads
496
+ `github.labels.build.done` (or the Jira/Linear equivalent), which is
497
+ environment-aware — a repository whose target is `dev` has a different
498
+ terminal role than one merging to production, and a hardcoded label would
499
+ apply the wrong one while looking correct in the repository it was written
500
+ in.
501
+ 2. **It refuses without evidence.** Completion requires a merged pull request in
502
+ the same repository. A command that closes whatever it is pointed at is a way
503
+ to make unfinished work disappear, and the closure is indistinguishable from
504
+ a real one afterwards. Cross-repository references do not count: a downstream
505
+ consumer's PR mentioning an upstream issue is not evidence the upstream issue
506
+ shipped.
507
+ 3. **It is idempotent**, so re-running after a retry converges rather than
508
+ accumulating.
509
+
510
+ It also removes the claimed role rather than only adding the terminal one.
511
+ Leaving both produces exactly the drift this step exists to end — an item that
512
+ is closed *and* still reports as in progress.
513
+
514
+ If the command refuses, do not close by hand. A refusal means the evidence is
515
+ not there, and a lifecycle step performed by hand is one nothing can verify
516
+ happened.
517
+
518
+ **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
519
+ reports every claimed item that already has a merged pull request, and
520
+ `--apply` completes them. Reporting is the default deliberately — a sweep that
521
+ closes things as a side effect of being run is not one anyone runs twice.
522
+
473
523
  ## 4. Terminal states
474
524
 
475
525
  Loop until one of:
@@ -470,6 +470,56 @@ matching Phase 4b's safe default. The full procedure is `lisa-linear-sync`
470
470
  Phase 4b; when the caller's flow already runs a `pr-merged` sync for this
471
471
  merge, confirming that sync ran satisfies this step.
472
472
 
473
+ ### d. Complete the work item
474
+
475
+ Run this once the PR reports `MERGED` and the ancestry check has passed:
476
+
477
+ ```
478
+ node scripts/lisa-work-item.mjs complete --ref <work-item>
479
+ ```
480
+
481
+ resolving the script the usual three ways (installed package, host `scripts/`,
482
+ this repo's own tree).
483
+
484
+ **This step exists because the previous arrangement did not work, measured.**
485
+ The instruction to move a work item to its terminal role lived in
486
+ `lisa-git-submit-pr`, in a section reached *after* that skill delegates the
487
+ entire merge loop to this one. So the skill that observes the merge was never
488
+ told to close anything, and the skill that was told had already handed off. The
489
+ result: **27 of 27** open items carrying the claimed role in this repository had
490
+ a merged pull request — the claimed lane reported 27 things in flight when the
491
+ real number was one.
492
+
493
+ Three properties make the command safe to run unconditionally:
494
+
495
+ 1. **The terminal role is RESOLVED, never assumed.** `lifecycleContract` reads
496
+ `github.labels.build.done` (or the Jira/Linear equivalent), which is
497
+ environment-aware — a repository whose target is `dev` has a different
498
+ terminal role than one merging to production, and a hardcoded label would
499
+ apply the wrong one while looking correct in the repository it was written
500
+ in.
501
+ 2. **It refuses without evidence.** Completion requires a merged pull request in
502
+ the same repository. A command that closes whatever it is pointed at is a way
503
+ to make unfinished work disappear, and the closure is indistinguishable from
504
+ a real one afterwards. Cross-repository references do not count: a downstream
505
+ consumer's PR mentioning an upstream issue is not evidence the upstream issue
506
+ shipped.
507
+ 3. **It is idempotent**, so re-running after a retry converges rather than
508
+ accumulating.
509
+
510
+ It also removes the claimed role rather than only adding the terminal one.
511
+ Leaving both produces exactly the drift this step exists to end — an item that
512
+ is closed *and* still reports as in progress.
513
+
514
+ If the command refuses, do not close by hand. A refusal means the evidence is
515
+ not there, and a lifecycle step performed by hand is one nothing can verify
516
+ happened.
517
+
518
+ **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
519
+ reports every claimed item that already has a merged pull request, and
520
+ `--apply` completes them. Reporting is the default deliberately — a sweep that
521
+ closes things as a side effect of being run is not one anyone runs twice.
522
+
473
523
  ## 4. Terminal states
474
524
 
475
525
  Loop until one of:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -470,6 +470,56 @@ matching Phase 4b's safe default. The full procedure is `lisa-linear-sync`
470
470
  Phase 4b; when the caller's flow already runs a `pr-merged` sync for this
471
471
  merge, confirming that sync ran satisfies this step.
472
472
 
473
+ ### d. Complete the work item
474
+
475
+ Run this once the PR reports `MERGED` and the ancestry check has passed:
476
+
477
+ ```
478
+ node scripts/lisa-work-item.mjs complete --ref <work-item>
479
+ ```
480
+
481
+ resolving the script the usual three ways (installed package, host `scripts/`,
482
+ this repo's own tree).
483
+
484
+ **This step exists because the previous arrangement did not work, measured.**
485
+ The instruction to move a work item to its terminal role lived in
486
+ `lisa-git-submit-pr`, in a section reached *after* that skill delegates the
487
+ entire merge loop to this one. So the skill that observes the merge was never
488
+ told to close anything, and the skill that was told had already handed off. The
489
+ result: **27 of 27** open items carrying the claimed role in this repository had
490
+ a merged pull request — the claimed lane reported 27 things in flight when the
491
+ real number was one.
492
+
493
+ Three properties make the command safe to run unconditionally:
494
+
495
+ 1. **The terminal role is RESOLVED, never assumed.** `lifecycleContract` reads
496
+ `github.labels.build.done` (or the Jira/Linear equivalent), which is
497
+ environment-aware — a repository whose target is `dev` has a different
498
+ terminal role than one merging to production, and a hardcoded label would
499
+ apply the wrong one while looking correct in the repository it was written
500
+ in.
501
+ 2. **It refuses without evidence.** Completion requires a merged pull request in
502
+ the same repository. A command that closes whatever it is pointed at is a way
503
+ to make unfinished work disappear, and the closure is indistinguishable from
504
+ a real one afterwards. Cross-repository references do not count: a downstream
505
+ consumer's PR mentioning an upstream issue is not evidence the upstream issue
506
+ shipped.
507
+ 3. **It is idempotent**, so re-running after a retry converges rather than
508
+ accumulating.
509
+
510
+ It also removes the claimed role rather than only adding the terminal one.
511
+ Leaving both produces exactly the drift this step exists to end — an item that
512
+ is closed *and* still reports as in progress.
513
+
514
+ If the command refuses, do not close by hand. A refusal means the evidence is
515
+ not there, and a lifecycle step performed by hand is one nothing can verify
516
+ happened.
517
+
518
+ **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
519
+ reports every claimed item that already has a merged pull request, and
520
+ `--apply` completes them. Reporting is the default deliberately — a sweep that
521
+ closes things as a side effect of being run is not one anyone runs twice.
522
+
473
523
  ## 4. Terminal states
474
524
 
475
525
  Loop until one of:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -470,6 +470,56 @@ matching Phase 4b's safe default. The full procedure is `lisa-linear-sync`
470
470
  Phase 4b; when the caller's flow already runs a `pr-merged` sync for this
471
471
  merge, confirming that sync ran satisfies this step.
472
472
 
473
+ ### d. Complete the work item
474
+
475
+ Run this once the PR reports `MERGED` and the ancestry check has passed:
476
+
477
+ ```
478
+ node scripts/lisa-work-item.mjs complete --ref <work-item>
479
+ ```
480
+
481
+ resolving the script the usual three ways (installed package, host `scripts/`,
482
+ this repo's own tree).
483
+
484
+ **This step exists because the previous arrangement did not work, measured.**
485
+ The instruction to move a work item to its terminal role lived in
486
+ `lisa-git-submit-pr`, in a section reached *after* that skill delegates the
487
+ entire merge loop to this one. So the skill that observes the merge was never
488
+ told to close anything, and the skill that was told had already handed off. The
489
+ result: **27 of 27** open items carrying the claimed role in this repository had
490
+ a merged pull request — the claimed lane reported 27 things in flight when the
491
+ real number was one.
492
+
493
+ Three properties make the command safe to run unconditionally:
494
+
495
+ 1. **The terminal role is RESOLVED, never assumed.** `lifecycleContract` reads
496
+ `github.labels.build.done` (or the Jira/Linear equivalent), which is
497
+ environment-aware — a repository whose target is `dev` has a different
498
+ terminal role than one merging to production, and a hardcoded label would
499
+ apply the wrong one while looking correct in the repository it was written
500
+ in.
501
+ 2. **It refuses without evidence.** Completion requires a merged pull request in
502
+ the same repository. A command that closes whatever it is pointed at is a way
503
+ to make unfinished work disappear, and the closure is indistinguishable from
504
+ a real one afterwards. Cross-repository references do not count: a downstream
505
+ consumer's PR mentioning an upstream issue is not evidence the upstream issue
506
+ shipped.
507
+ 3. **It is idempotent**, so re-running after a retry converges rather than
508
+ accumulating.
509
+
510
+ It also removes the claimed role rather than only adding the terminal one.
511
+ Leaving both produces exactly the drift this step exists to end — an item that
512
+ is closed *and* still reports as in progress.
513
+
514
+ If the command refuses, do not close by hand. A refusal means the evidence is
515
+ not there, and a lifecycle step performed by hand is one nothing can verify
516
+ happened.
517
+
518
+ **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
519
+ reports every claimed item that already has a merged pull request, and
520
+ `--apply` completes them. Reporting is the default deliberately — a sweep that
521
+ closes things as a side effect of being run is not one anyone runs twice.
522
+
473
523
  ## 4. Terminal states
474
524
 
475
525
  Loop until one of:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -470,6 +470,56 @@ matching Phase 4b's safe default. The full procedure is `lisa-linear-sync`
470
470
  Phase 4b; when the caller's flow already runs a `pr-merged` sync for this
471
471
  merge, confirming that sync ran satisfies this step.
472
472
 
473
+ ### d. Complete the work item
474
+
475
+ Run this once the PR reports `MERGED` and the ancestry check has passed:
476
+
477
+ ```
478
+ node scripts/lisa-work-item.mjs complete --ref <work-item>
479
+ ```
480
+
481
+ resolving the script the usual three ways (installed package, host `scripts/`,
482
+ this repo's own tree).
483
+
484
+ **This step exists because the previous arrangement did not work, measured.**
485
+ The instruction to move a work item to its terminal role lived in
486
+ `lisa-git-submit-pr`, in a section reached *after* that skill delegates the
487
+ entire merge loop to this one. So the skill that observes the merge was never
488
+ told to close anything, and the skill that was told had already handed off. The
489
+ result: **27 of 27** open items carrying the claimed role in this repository had
490
+ a merged pull request — the claimed lane reported 27 things in flight when the
491
+ real number was one.
492
+
493
+ Three properties make the command safe to run unconditionally:
494
+
495
+ 1. **The terminal role is RESOLVED, never assumed.** `lifecycleContract` reads
496
+ `github.labels.build.done` (or the Jira/Linear equivalent), which is
497
+ environment-aware — a repository whose target is `dev` has a different
498
+ terminal role than one merging to production, and a hardcoded label would
499
+ apply the wrong one while looking correct in the repository it was written
500
+ in.
501
+ 2. **It refuses without evidence.** Completion requires a merged pull request in
502
+ the same repository. A command that closes whatever it is pointed at is a way
503
+ to make unfinished work disappear, and the closure is indistinguishable from
504
+ a real one afterwards. Cross-repository references do not count: a downstream
505
+ consumer's PR mentioning an upstream issue is not evidence the upstream issue
506
+ shipped.
507
+ 3. **It is idempotent**, so re-running after a retry converges rather than
508
+ accumulating.
509
+
510
+ It also removes the claimed role rather than only adding the terminal one.
511
+ Leaving both produces exactly the drift this step exists to end — an item that
512
+ is closed *and* still reports as in progress.
513
+
514
+ If the command refuses, do not close by hand. A refusal means the evidence is
515
+ not there, and a lifecycle step performed by hand is one nothing can verify
516
+ happened.
517
+
518
+ **The backstop, for the ones that still slip:** `lisa-work-item.mjs sweep`
519
+ reports every claimed item that already has a merged pull request, and
520
+ `--apply` completes them. Reporting is the default deliberately — a sweep that
521
+ closes things as a side effect of being run is not one anyone runs twice.
522
+
473
523
  ## 4. Terminal states
474
524
 
475
525
  Loop until one of:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.41.1",
3
+ "version": "3.43.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"