@codyswann/lisa 4.33.5 → 4.33.8
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/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +8 -1
- package/dist/cli/apply.js.map +1 -1
- package/dist/core/apply-receipt.d.ts +18 -0
- package/dist/core/apply-receipt.d.ts.map +1 -1
- package/dist/core/apply-receipt.js +7 -5
- package/dist/core/apply-receipt.js.map +1 -1
- package/dist/core/config.d.ts +34 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +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/lisa.d.ts +75 -0
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +125 -6
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +22 -7
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/core/workflow-deletion-ownership.d.ts +69 -0
- package/dist/core/workflow-deletion-ownership.d.ts.map +1 -0
- package/dist/core/workflow-deletion-ownership.js +156 -0
- package/dist/core/workflow-deletion-ownership.js.map +1 -0
- 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-intake-explain/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/lisa/.codex-plugin/skills/lisa-ticket-triage/SKILL.md +1 -1
- package/plugins/lisa/rules/eager/blocker-containment.md +44 -0
- package/plugins/lisa/rules/reference/blocker-containment.md +92 -0
- package/plugins/lisa/skills/lisa-intake-explain/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/lisa/skills/lisa-ticket-triage/SKILL.md +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-intake-explain/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/lisa-agy/skills/lisa-ticket-triage/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/rules/eager/blocker-containment.md +44 -0
- package/plugins/lisa-copilot/rules/reference/blocker-containment.md +92 -0
- package/plugins/lisa-copilot/skills/lisa-intake-explain/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/lisa-copilot/skills/lisa-ticket-triage/SKILL.md +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/blocker-containment-reference.mdc +97 -0
- package/plugins/lisa-cursor/rules/blocker-containment.mdc +49 -0
- package/plugins/lisa-cursor/skills/lisa-intake-explain/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/lisa-cursor/skills/lisa-ticket-triage/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/rules/eager/blocker-containment.md +44 -0
- package/plugins/src/base/rules/reference/blocker-containment.md +92 -0
- package/plugins/src/base/skills/lisa-intake-explain/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +32 -20
- package/plugins/src/base/skills/lisa-ticket-triage/SKILL.md +1 -1
- package/typescript/copy-overwrite/.github/GITHUB_ACTIONS.md +2 -2
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +414 -6
- package/typescript/create-only/.github/workflows/review-evidence.yml +78 -3
- package/typescript/deletions.json +8 -1
|
@@ -1005,28 +1005,40 @@ is no normalized `is blocked by` field. Read the bundle, then extract blockers p
|
|
|
1005
1005
|
- **Linear**: inspect the native issue **relations** from Linear MCP `get_issue` and select
|
|
1006
1006
|
blocker relations.
|
|
1007
1007
|
|
|
1008
|
-
Then classify each blocker
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1008
|
+
Then classify each blocker **by containment, per the `blocker-containment` rule** — cite that
|
|
1009
|
+
slug for the full contract; do not restate its resolution steps or its fail-closed table here.
|
|
1010
|
+
|
|
1011
|
+
The one question is whether the blocker's code is on the branch **this item** will be built from:
|
|
1012
|
+
resolve this item's base branch from its `## Target Backend Environment` through
|
|
1013
|
+
`.lisa.config.json` `deploy.branches` (`derived-branch-plan`, `config-resolution` — never by
|
|
1014
|
+
reading a rendered `Branch Plan`), resolve the blocker's merge commit through the vendor access
|
|
1015
|
+
layer, and test ancestry against the remote.
|
|
1016
|
+
|
|
1017
|
+
- **Contained** — every merged PR on the blocker is an ancestor of this item's base branch →
|
|
1018
|
+
**cleared**.
|
|
1019
|
+
- **Not contained**, or containment **not computable** for any reason the rule enumerates (no
|
|
1020
|
+
linked PR, PR not merged, blocker inaccessible, base branch underivable or absent from the
|
|
1021
|
+
remote, ancestry query errored) → **still blocking**, with the rule's reason key in the run
|
|
1022
|
+
record.
|
|
1023
|
+
- **Ships no code** — the blocker is closed as completed, positively declares
|
|
1024
|
+
`Target Backend Environment: None — no runtime behavior change`, and has no merged PR →
|
|
1025
|
+
**cleared** under the rule's carve-out. This is a positive determination, never an absence:
|
|
1026
|
+
"no PR found" is `no-pr` and stays blocking.
|
|
1027
|
+
- **Human override** — an explicit human statement on this item that the dependency is satisfied
|
|
1028
|
+
clears it and outranks a failed containment check. Name it in the run record.
|
|
1029
|
+
|
|
1030
|
+
**A lifecycle status name is never the test.** `done`, `on-stg`, `on-dev` and `code-review` are
|
|
1031
|
+
labels that correlate with containment in a single-trunk workflow and come apart from it in a
|
|
1032
|
+
promotion workflow, where `main` is the trailing branch — a blocker at `On Stg` is on `staging`
|
|
1033
|
+
and absent from `main`, and a blocker in `Code Review` is merged to nothing. Containment gets
|
|
1034
|
+
both the loose and the strict failure right at once: a blocker merged into the branch this item
|
|
1035
|
+
builds from clears **immediately**, with no wait for production, so a dependent is never stranded
|
|
1036
|
+
behind work already merged where it needs it.
|
|
1026
1037
|
|
|
1027
1038
|
Only re-dispatch when **every** parsed blocker is cleared. When in doubt, stay blocked — a
|
|
1028
|
-
false-negative (left blocked) is
|
|
1029
|
-
|
|
1039
|
+
false-negative (left blocked) costs one cycle and is visible; a false-positive is **invisible**,
|
|
1040
|
+
because the item moves to `ready`, which looks exactly like a repair that worked, and the failure
|
|
1041
|
+
surfaces days later as work begun before its dependency existed.
|
|
1030
1042
|
|
|
1031
1043
|
### Class B — validation / quality-gate self-block
|
|
1032
1044
|
|
|
@@ -46,7 +46,7 @@ If relevant code IS found, proceed to Phase 2.
|
|
|
46
46
|
|
|
47
47
|
From the context bundle, evaluate relationships before analyzing this ticket in isolation:
|
|
48
48
|
|
|
49
|
-
- **Open blockers (`is blocked by`)**:
|
|
49
|
+
- **Open blockers (`is blocked by`)**: clear-check each blocker **by containment, per the `blocker-containment` rule** — cite that slug for the full contract rather than restating it. A blocker is satisfied when its merged PRs are ancestors of the branch this ticket will be built from (resolved from this ticket's `## Target Backend Environment` through `.lisa.config.json` `deploy.branches`, never from a rendered `Branch Plan`), or when the rule's narrow no-code carve-out or human override applies. **`Done` is not the test** — a state name holds in a single-trunk workflow and comes apart in a promotion workflow, and requiring the production terminal strands every dependent behind work already merged where this ticket needs it. This is the same predicate the `DUPLICATE_ALREADY_FIXED` bullet below already requires ("present on the relevant base branch… never from a name/label match alone"), applied here too. For a blocker that is not contained, or where containment is not computable, raise an ambiguity naming the rule's reason key: "Blocker {KEY} is not on {BRANCH} ({REASON}) — work cannot meaningfully start." That is an automatic `BLOCKED` verdict unless the human confirms the blocker state is acceptable.
|
|
50
50
|
- **Epic siblings in progress**: if a sibling under the same epic is `In Progress` / `In Review` with a different assignee and overlapping scope, raise it as an edge case in Phase 4 ("Duplicate-work risk with {KEY}").
|
|
51
51
|
- **`duplicates` / `is duplicated by` links**:
|
|
52
52
|
- If this ticket is a duplicate of an open canonical ticket whose fix is not yet merged into the base branch, verdict is `BLOCKED` with the recommendation to close as duplicate manually rather than implement.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.8",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.8",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.8",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.8",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.8",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Blocker Containment (load-bearing)
|
|
2
|
+
|
|
3
|
+
**An `is blocked by` dependency is cleared by one fact only: the blocker's code is present on the branch the dependent will be built from.** A lifecycle status name — `done`, `on-stg`, `on-dev`, `code-review`, or any configured equivalent — is never that fact. It is a label that correlates with the fact in a single-trunk workflow and comes apart from it in a promotion workflow.
|
|
4
|
+
|
|
5
|
+
**One vendor-neutral contract, cited by** `lisa-repair-intake` (Class A dependency clearing), `lisa-intake-explain` (dependency hold gate), and `lisa-ticket-triage` (Phase 1.5 open-blocker gate) — the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent per-consumer prose.
|
|
6
|
+
|
|
7
|
+
## Why a status name is the wrong test
|
|
8
|
+
|
|
9
|
+
Where promotion runs `dev → staging → main`, `main` is the **trailing** branch. A blocker at `On Stg` is merged to `staging` and absent from `main`, so a dependent that builds from `main` does not have it. A blocker in `Code Review` is merged to nothing at all. The two failures are symmetric and both are live in the tree:
|
|
10
|
+
|
|
11
|
+
- Test too **loosely** (any env-staged `done` clears) and a dependent is dispatched into work whose prerequisite is not on its branch. Nothing goes red; the item moves to `ready`, which is indistinguishable from a repair that worked.
|
|
12
|
+
- Test too **strictly** (only the production terminal clears) and every dependent strands behind a blocker that is already merged where it matters.
|
|
13
|
+
|
|
14
|
+
Containment is the one predicate that gets both right, because it asks the question the status name was standing in for.
|
|
15
|
+
|
|
16
|
+
## The predicate
|
|
17
|
+
|
|
18
|
+
1. **Resolve the dependent's base branch.** Take the dependent's `## Target Backend Environment` and map that exact key forward through `.lisa.config.json` `deploy.branches`, per `derived-branch-plan` and `config-resolution`. **Never read the rendered `Branch Plan` section as input** — `derived-branch-plan` forbids it; a surface needing the base branch derives it again.
|
|
19
|
+
2. **Resolve the blocker's merge commit** — the commit that landed the blocker's PR.
|
|
20
|
+
3. **Test ancestry on the remote:** is that commit an ancestor of the resolved base branch (`git merge-base --is-ancestor <sha> origin/<base>`)?
|
|
21
|
+
4. **Cleared** iff the answer is yes. Otherwise **still blocking**.
|
|
22
|
+
|
|
23
|
+
## Fail closed
|
|
24
|
+
|
|
25
|
+
**Never fail open on an unresolvable ancestry check.** Any step that cannot be positively resolved leaves the dependency **blocking**, and the run record says which step failed:
|
|
26
|
+
|
|
27
|
+
- no linked PR, or a PR that is not merged;
|
|
28
|
+
- the blocker is inaccessible (deleted, cross-org, permission denied) — containment across repositories is not defined;
|
|
29
|
+
- the dependent's environment is not derivable, maps non-uniquely, or maps to a branch absent from the remote;
|
|
30
|
+
- the ancestry query itself fails.
|
|
31
|
+
|
|
32
|
+
A false negative (left blocked) costs a cycle and is visible. A false positive (dispatched into a missing prerequisite) is invisible and surfaces days later as work begun before its dependency existed. Prefer the visible failure.
|
|
33
|
+
|
|
34
|
+
## The one carve-out: a blocker with no code
|
|
35
|
+
|
|
36
|
+
A dependency need not deliver code. A blocker **closed as completed** that positively declares no code deliverable — `Target Backend Environment: None — no runtime behavior change` (doc-only / config-only / type-only), no linked PR, no merge commit — is **cleared**, because there is no artifact whose location could matter.
|
|
37
|
+
|
|
38
|
+
This requires a **positive determination**, never an absence. "I could not find a PR" is fail-closed under the rule above; "the item declares it ships no code" is this carve-out. Collapsing the two turns the carve-out into a universal bypass.
|
|
39
|
+
|
|
40
|
+
## Human override
|
|
41
|
+
|
|
42
|
+
A human statement on the item that the dependency is satisfied clears it, and outranks a failed containment check. Record it as the reason in the run record. This is the escape hatch for cross-repo and untracked dependencies, and it is the only one. An automation may never synthesize one on its own behalf.
|
|
43
|
+
|
|
44
|
+
Full contract (resolution steps, per-vendor merge-commit bindings, the fail-closed reason-key table, the no-code carve-out's three conditions, and what this rule deliberately does not do): [reference/blocker-containment.md](../reference/blocker-containment.md).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Blocker Containment
|
|
2
|
+
|
|
3
|
+
An `is blocked by` link is a **development** dependency. It asks one question: *does the dependent have the blocker's code on the branch it will be built from?* This rule makes that the test, replacing the lifecycle-status-name checks that three Lisa surfaces had each grown independently.
|
|
4
|
+
|
|
5
|
+
It is a **single vendor-neutral contract** consumed by `lisa-repair-intake` (Class A dependency clearing), `lisa-intake-explain` (dependency hold gate), and `lisa-ticket-triage` (Phase 1.5 open-blocker gate). Each cites this slug rather than re-implementing the test, exactly as they cite `leaf-only-lifecycle` and `repo-scope-split`. That is what keeps the three from drifting apart again.
|
|
6
|
+
|
|
7
|
+
## The failure this replaces
|
|
8
|
+
|
|
9
|
+
Before this rule, all three surfaces tested a **status name** and each picked a different threshold, so the same dependency could be simultaneously cleared by one surface and held by another.
|
|
10
|
+
|
|
11
|
+
`lisa-repair-intake` tested too loosely — any env-staged `done` role cleared the dependency, on the stated reasoning that `On Dev`, `On Stg` and production `Done` all mean *"the blocker's code is merged and deployed to ≥1 environment"*, and that an `is blocked by` link *"is satisfied once the blocker's code is in trunk."* The first clause names the right idea. The second substitutes a weaker one for it: in a `dev → staging → main` promotion workflow, "deployed to ≥1 environment" and "in trunk" are different facts. `main` is the trailing branch, so a blocker at `On Stg` is on `staging` and absent from `main`. A dependent building from `main` was dispatched without it. That surface also cleared on a post-build `review` role, where the code is merged to no branch at all.
|
|
12
|
+
|
|
13
|
+
`lisa-ticket-triage` tested too strictly — a blocker not at the production terminal raised an automatic `BLOCKED` verdict, stranding dependents behind work already merged where they needed it.
|
|
14
|
+
|
|
15
|
+
Both are the same defect. **A lifecycle status name is never the test** — not `status:done`, not `status:on-stg`, not `status:on-dev`, and least of all `status:code-review`, where the code is merged to no branch at all. The status name is a **proxy** for branch containment that holds in a single-trunk workflow and breaks in a promotion workflow, and the two surfaces broke in opposite directions because they picked opposite ends of the same bad proxy. Correcting either one alone by moving its threshold re-opens the other's bug — which is why this is one predicate rather than three tuned lists.
|
|
16
|
+
|
|
17
|
+
Notably, the correct predicate was already in the tree. `lisa-ticket-triage`'s own `DUPLICATE_ALREADY_FIXED` bullet, four lines below its defective gate, already required *"empirical evidence that the canonical fix is present on the relevant base branch. Never emit this verdict from a name/label match alone."* This rule generalizes that sentence to the gate above it.
|
|
18
|
+
|
|
19
|
+
## The predicate
|
|
20
|
+
|
|
21
|
+
### 1. Resolve the dependent's base branch
|
|
22
|
+
|
|
23
|
+
Take the **dependent's** `## Target Backend Environment`, resolve it under the existing grammar (`config-resolution`, `pre-flight-autofill` — human-confirmed wins, then validated `Inferred:`, then `Assumption:`), and map that exact key forward through `.lisa.config.json` `deploy.branches`.
|
|
24
|
+
|
|
25
|
+
**Never read the rendered `Branch Plan` section as input.** `derived-branch-plan` is explicit that the rendered branches exist for humans and are recomputed for machines: *"The rendered branches are never read as input by any flow, at any phase — a surface that needs the base branch derives it again from the environment."* Reading the rendered section would make a hand-edited ticket able to redirect the containment test.
|
|
26
|
+
|
|
27
|
+
The key must map uniquely, and the mapped branch must exist on the remote. Neither condition is assumed.
|
|
28
|
+
|
|
29
|
+
### 2. Resolve the blocker's merge commit
|
|
30
|
+
|
|
31
|
+
The commit that landed the blocker's PR, obtained through the vendor access layer (`integration-access-layer`), never a direct vendor API call:
|
|
32
|
+
|
|
33
|
+
- **GitHub** — the merge commit of the PR linked from the blocker issue.
|
|
34
|
+
- **JIRA** — the merge commit of the PR on the blocker's development panel / linked PR records.
|
|
35
|
+
- **Linear** — the merge commit of the PR attached to the blocker Issue.
|
|
36
|
+
|
|
37
|
+
An unmerged PR has no merge commit and is not a candidate. A blocker with several merged PRs requires **all** of them contained.
|
|
38
|
+
|
|
39
|
+
### 3. Test ancestry against the remote
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git fetch origin <base> --quiet
|
|
43
|
+
git merge-base --is-ancestor <blocker-merge-sha> origin/<base>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Exit `0` → contained. Exit `1` → not contained. Any other exit → not computable, which is **not** the same as not contained; both block, but the run record distinguishes them so an operator can tell a genuine dependency from a broken lookup.
|
|
47
|
+
|
|
48
|
+
### 4. Verdict
|
|
49
|
+
|
|
50
|
+
**Cleared** iff every resolved merge commit is an ancestor of the resolved base branch. Otherwise **still blocking**.
|
|
51
|
+
|
|
52
|
+
## Fail closed
|
|
53
|
+
|
|
54
|
+
**Never fail open on an unresolvable ancestry check.** Each of these leaves the dependency blocking, and each is named distinctly in the run record:
|
|
55
|
+
|
|
56
|
+
| Condition | Verdict | Run-record reason |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| Blocker has no linked PR | still blocking | `no-pr` |
|
|
59
|
+
| Linked PR is open or closed unmerged | still blocking | `pr-not-merged` |
|
|
60
|
+
| Blocker inaccessible (deleted, cross-org, permission denied) | still blocking | `blocker-inaccessible` |
|
|
61
|
+
| Dependent's environment not derivable or non-unique | still blocking | `base-branch-underivable` |
|
|
62
|
+
| Mapped branch absent from the remote | still blocking | `base-branch-missing` |
|
|
63
|
+
| Ancestry query errored | still blocking | `containment-not-computable` |
|
|
64
|
+
| Merge commit resolved, not an ancestor | still blocking | `not-contained` |
|
|
65
|
+
|
|
66
|
+
The asymmetry is deliberate and is the same one the consuming surfaces already state: a false negative (left blocked) costs one cycle and an operator can see it sitting there. A false positive (re-dispatched into a prerequisite that is not on the branch) is **invisible** — the item moves to `ready`, which looks exactly like a repair that worked, and the failure surfaces days later as work that began before its dependency existed. Prefer the visible failure. When in doubt, stay blocked.
|
|
67
|
+
|
|
68
|
+
## The one carve-out: a blocker that ships no code
|
|
69
|
+
|
|
70
|
+
Not every dependency delivers code. A decision, a doc change, or a config-only item can legitimately block work and will never have a merge commit on any branch. Requiring containment of a non-existent artifact would strand those dependents permanently — the same bug, in a new place.
|
|
71
|
+
|
|
72
|
+
A blocker is cleared under this carve-out when **all** of the following hold:
|
|
73
|
+
|
|
74
|
+
1. it is **closed as completed** (not `not planned` — see `rejection-detection`, where *not planned* is a durable decline);
|
|
75
|
+
2. it **positively declares no code deliverable** — `## Target Backend Environment` reading `None — no runtime behavior change: doc-only` (or `config-only` / `type-only`), per the grammar `derived-branch-plan` already defines;
|
|
76
|
+
3. it has **no linked merged PR**. If it has one, the normal containment test applies regardless of what the section declares — the code is the authority over the prose.
|
|
77
|
+
|
|
78
|
+
This is a **positive determination, never an absence.** "I could not find a PR" is `no-pr` in the fail-closed table above. "The item declares that it ships no code" is this carve-out. Collapsing the two converts a narrow carve-out into a universal bypass, and it is the single most likely way for this rule to be implemented wrongly.
|
|
79
|
+
|
|
80
|
+
An Epic or container blocker declaring `None — container: state rolls up from children` is **not** covered: resolve containment against its children, or treat it as `blocker-inaccessible` if the children cannot be enumerated.
|
|
81
|
+
|
|
82
|
+
## Human override
|
|
83
|
+
|
|
84
|
+
An explicit human statement on the dependent — in the body or in a comment — that the dependency is satisfied clears it, and outranks a failed containment check. The run record names the override and the comment it came from.
|
|
85
|
+
|
|
86
|
+
This is the sanctioned escape hatch for the cases the predicate genuinely cannot reach: a cross-repository blocker (two repositories have no shared ancestry, so containment is undefined), a dependency on work tracked outside any connected system, or a vendor whose PR linkage is unreadable. It is the **only** escape hatch. An automation may never synthesize one on its own behalf.
|
|
87
|
+
|
|
88
|
+
## What this rule does not do
|
|
89
|
+
|
|
90
|
+
It does not decide **when** a dependency edge should exist, dissolve an edge once satisfied, or govern what a surface does after clearing — re-dispatch, re-validation, and lifecycle transitions remain the consuming skill's business. It governs exactly one question: given an edge, is it satisfied.
|
|
91
|
+
|
|
92
|
+
It also carries no memory of its own prior verdicts. A surface that re-runs this predicate after a human reversed a transition it produced will reach the same answer from the same facts; making that reversal durable is a separate concern belonging to the consuming skill's loop-prevention contract, not to this predicate.
|
|
@@ -190,7 +190,7 @@ Apply gate verdicts in the same order as build intake:
|
|
|
190
190
|
|
|
191
191
|
1. **Repo-scope gate.** A build item carrying `repo:<other>` and not `repo:<current>` is outside this repo's pickup lane. Return `MISCONFIGURED` when repo scope is absent or contradictory enough that the current repo cannot be determined confidently; otherwise explain the repo-scope mismatch and recommend running intake in the target repo or fixing the `repo:<name>` label. For an unlabeled item whose target repo is obvious from the item body, report the inferred repo signal but stay read-only: execution intake would stamp `repo:<name>`, while intake-explain only says it would do so. A multi-repo leaf is not directly buildable; explain that execution intake would split it per `repo-scope-split`, but this read-only diagnosis does not split.
|
|
192
192
|
2. **Leaf-only gate.** If the item has open child work from native GitHub sub-issues or body parentage, return `NON_LEAF_CONTAINER` and explain that direct build pickup is leaf-only per `leaf-only-lifecycle`. If it has no open children but carries `type:Epic`, also return `NON_LEAF_CONTAINER` because a childless Epic is a pure rollup container that still needs decomposition or reclassification. (A childless `type:Story` or `type:Spike` is a valid leaf per `leaf-only-lifecycle` and is NOT a `NON_LEAF_CONTAINER` — it is directly buildable.) The next action for a childless Epic is decomposition, moving `status:ready` to leaf children, or correcting the issue type. Execution build intake would move such a stale ready container out of the pickup queue; this read-only diagnosis must not perform that repair.
|
|
193
|
-
3. **Dependency hold gate.** If a single-repo leaf for the current repo has explicit blockers, read each blocker
|
|
193
|
+
3. **Dependency hold gate.** If a single-repo leaf for the current repo has explicit blockers, read each blocker and clear-check it **by containment, per the `blocker-containment` rule** — cite that slug for the full contract rather than restating it. A blocker is clear only when its merged PRs are ancestors of the branch this leaf will be built from (resolved from the leaf's `## Target Backend Environment` through `.lisa.config.json` `deploy.branches`, never from a rendered `Branch Plan`), or when the rule's narrow no-code carve-out or human override applies. **A build status label is never the test** — `status:code-review`, `status:on-dev`, `status:on-stg` and `status:done` are name matches that hold in a single-trunk workflow and come apart in a promotion workflow, where `main` is the trailing branch. Every other state, and every case where containment is not computable, is active: fail closed. Return `HELD_BY_BLOCKERS`, list the active blocker refs with the rule's reason key for each, and make the next action blocker resolution rather than `/lisa:intake`.
|
|
194
194
|
4. **Ready leaf.** A build item in the configured ready role, scoped to the current repo, with no open child work and no active blockers returns `ELIGIBLE_FOR_INTAKE`. The `Why:` line should say it is a single-repo leaf for the current repo and that leaf-only, repo-scope, and dependency gates all pass.
|
|
195
195
|
|
|
196
196
|
Relevant `Signals:` should include the decisive context, not every field: for example `repo:lisa; type:Sub-task; no open children`, `open children #12/#13`, `repo:api but current repo is web`, or `active blockers CodySwannGT/lisa#123`.
|