@codyswann/lisa 2.255.0 → 2.256.1
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/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +9 -3
- package/plugins/lisa/agents/github-agent.md +2 -2
- package/plugins/lisa/agents/jira-agent.md +2 -2
- package/plugins/lisa/agents/linear-agent.md +2 -2
- package/plugins/lisa/rules/eager/config-resolution.md +27 -8
- package/plugins/lisa/rules/eager/pre-flight-autofill.md +21 -7
- package/plugins/lisa/rules/reference/config-resolution.md +55 -16
- package/plugins/lisa/rules/reference/pre-flight-autofill.md +23 -8
- package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +64 -8
- package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +61 -5
- package/plugins/lisa/scripts/automation-status-report.mjs +30 -0
- package/plugins/lisa/scripts/automation-status-run-history.mjs +185 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/lisa/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +9 -3
- package/plugins/lisa-agy/agents/github-agent.md +2 -2
- package/plugins/lisa-agy/agents/jira-agent.md +2 -2
- package/plugins/lisa-agy/agents/linear-agent.md +2 -2
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +64 -8
- package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +61 -5
- package/plugins/lisa-agy/scripts/automation-status-report.mjs +30 -0
- package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +185 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +9 -3
- 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/agents/github-agent.agent.md +2 -2
- package/plugins/lisa-copilot/agents/jira-agent.agent.md +2 -2
- package/plugins/lisa-copilot/agents/linear-agent.agent.md +2 -2
- package/plugins/lisa-copilot/rules/eager/config-resolution.md +27 -8
- package/plugins/lisa-copilot/rules/eager/pre-flight-autofill.md +21 -7
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +55 -16
- package/plugins/lisa-copilot/rules/reference/pre-flight-autofill.md +23 -8
- package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +64 -8
- package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +61 -5
- package/plugins/lisa-copilot/scripts/automation-status-report.mjs +30 -0
- package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +185 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +9 -3
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/github-agent.md +2 -2
- package/plugins/lisa-cursor/agents/jira-agent.md +2 -2
- package/plugins/lisa-cursor/agents/linear-agent.md +2 -2
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +55 -16
- package/plugins/lisa-cursor/rules/config-resolution.mdc +27 -8
- package/plugins/lisa-cursor/rules/pre-flight-autofill-reference.mdc +23 -8
- package/plugins/lisa-cursor/rules/pre-flight-autofill.mdc +21 -7
- package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +64 -8
- package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +61 -5
- package/plugins/lisa-cursor/scripts/automation-status-report.mjs +30 -0
- package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +185 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +9 -3
- 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/agents/github-agent.md +2 -2
- package/plugins/src/base/agents/jira-agent.md +2 -2
- package/plugins/src/base/agents/linear-agent.md +2 -2
- package/plugins/src/base/rules/eager/config-resolution.md +27 -8
- package/plugins/src/base/rules/eager/pre-flight-autofill.md +21 -7
- package/plugins/src/base/rules/reference/config-resolution.md +55 -16
- package/plugins/src/base/rules/reference/pre-flight-autofill.md +23 -8
- package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +64 -8
- package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +61 -5
- package/plugins/src/base/scripts/automation-status-report.mjs +30 -0
- package/plugins/src/base/scripts/automation-status-run-history.mjs +185 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +11 -3
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +9 -3
- package/plugins/src/base/skills/lisa-implement/SKILL.md +6 -2
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +9 -6
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +9 -3
|
@@ -35,7 +35,7 @@ Required fields (stop and ask if missing — do not invent values):
|
|
|
35
35
|
| Priority | CREATE | Default to project default if unstated |
|
|
36
36
|
| Acceptance criteria | Story, Task, Bug, Sub-task, Improvement | Gherkin — see Phase 3 |
|
|
37
37
|
| Validation Journey | Runtime-behavior changes | Delegate to `/jira-add-journey` |
|
|
38
|
-
| Target backend environment | Runtime-behavior changes | `
|
|
38
|
+
| Target backend environment | Runtime-behavior changes | For every work type, use an exact `deploy.branches` key when an environment is known. Human: bare key or `Confirmed: <env>`. Automation: `Inferred: <env> — evidence: <title\|body\|reproduction\|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. |
|
|
39
39
|
| Sign-in account / credentials | Tickets that touch authenticated surfaces | Name the account (or source — 1Password item, env var, seeded fixture) and role; recorded in description (Phase 3). Omit when sign-in is not required. |
|
|
40
40
|
| Single-repo scope | Bug, Task, Sub-task, Improvement | These leaf work units MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
|
|
41
41
|
| Source Requirement | PRD-sourced tickets (`prd_source` provided) | `h2. Source Requirement` with PRD link + verbatim requirement quote(s) — see Phase 3; enforced at every level, sub-tasks included |
|
|
@@ -83,11 +83,14 @@ h2. Out of Scope
|
|
|
83
83
|
[Explicit list of what this ticket does NOT cover. Forces scope discipline.]
|
|
84
84
|
|
|
85
85
|
h2. Target Backend Environment
|
|
86
|
-
[Required when the ticket changes runtime behavior.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
[Required when the ticket changes runtime behavior. Use an exact
|
|
87
|
+
`deploy.branches` key. A human-confirmed value is a bare key or
|
|
88
|
+
`Confirmed: <env>`. An automated evidence write is
|
|
89
|
+
`Inferred: <env> — evidence: <title|body|reproduction|hostname>`; an automated
|
|
90
|
+
generic default is `Assumption: <env> — remote default branch <branch>`.
|
|
91
|
+
Without a unique reverse-map use `Assumption: remote default branch <branch>`.
|
|
92
|
+
Human confirmation replaces the automated annotation with a bare key or
|
|
93
|
+
`Confirmed: <env>`. Skip only for doc/config/type-only tickets.]
|
|
91
94
|
|
|
92
95
|
h2. Sign-in Required
|
|
93
96
|
[Include this section ONLY if the work touches authenticated surfaces.
|
|
@@ -160,7 +160,7 @@ A **build-ready leaf work unit** that is not part of an Epic context stands alon
|
|
|
160
160
|
|
|
161
161
|
#### S8 — Target Backend Environment
|
|
162
162
|
|
|
163
|
-
When `runtime_behavior_change = true`, description must contain `## Target Backend Environment
|
|
163
|
+
When `runtime_behavior_change = true`, the description must contain `## Target Backend Environment`. Read accepted environments from the exact configured keys of `.lisa.config.json` `deploy.branches`, never from a hardcoded list. Accept a human-confirmed bare exact configured key or `Confirmed: <env>`, automated `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, automated `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` when no unique reverse-map exists. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. For legacy bare values, use managed draft markers and current ticket content only; provider edit history is not required. A marker proves automation and requires re-annotation; otherwise unknown provenance plus conflicting evidence fails for confirmation. Validate the annotation shape/source and remote-default branch; validate `<env>` as an exact configured key whenever present. A valid branch-only assumption must not fail solely because its reverse-map is absent or ambiguous. Normalize built-in `prod` ↔ `production` only when exactly one of those keys is configured. No other aliases are valid. Skipped for doc-only / config-only / type-only / Epic.
|
|
164
164
|
|
|
165
165
|
#### S9 — Sign-in Required
|
|
166
166
|
|
|
@@ -57,7 +57,7 @@ Required fields (stop and ask if missing — never invent values):
|
|
|
57
57
|
| Priority | CREATE | Native Linear priority: 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low |
|
|
58
58
|
| Acceptance criteria | Story, Task, Bug, Sub-task, Improvement | Gherkin — see Phase 3 |
|
|
59
59
|
| Validation Journey | Runtime-behavior changes | Delegate to `/linear-add-journey` |
|
|
60
|
-
| Target backend environment | Runtime-behavior changes | `
|
|
60
|
+
| Target backend environment | Runtime-behavior changes | For every work type, use an exact `deploy.branches` key when an environment is known. Human: bare key or `Confirmed: <env>`. Automation: `Inferred: <env> — evidence: <title\|body\|reproduction\|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. |
|
|
61
61
|
| Sign-in account / credentials | Items that touch authenticated surfaces | Name the account (or source — 1Password item, env var, seeded fixture) and role; recorded in description. Omit when sign-in is not required. |
|
|
62
62
|
| Single-repo scope | Bug, Task, Sub-task | These types MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
|
|
63
63
|
| Source Requirement | PRD-sourced Issues (`prd_source` provided) | `## Source Requirement` with PRD link + verbatim requirement quote(s) — see Phase 3; enforced at every level, sub-issues included. |
|
|
@@ -103,8 +103,14 @@ Linear descriptions are markdown (NOT Jira wiki markup — no `h2.` headings, us
|
|
|
103
103
|
[Explicit list of what this item does NOT cover. Forces scope discipline.]
|
|
104
104
|
|
|
105
105
|
## Target Backend Environment
|
|
106
|
-
[Required when the item changes runtime behavior.
|
|
107
|
-
|
|
106
|
+
[Required when the item changes runtime behavior. Use an exact
|
|
107
|
+
`deploy.branches` key. A human-confirmed value is a bare key or
|
|
108
|
+
`Confirmed: <env>`. An automated evidence write is
|
|
109
|
+
`Inferred: <env> — evidence: <title|body|reproduction|hostname>`; an automated
|
|
110
|
+
generic default is `Assumption: <env> — remote default branch <branch>`.
|
|
111
|
+
Without a unique reverse-map use `Assumption: remote default branch <branch>`.
|
|
112
|
+
Human confirmation replaces the automated annotation with a bare key or
|
|
113
|
+
`Confirmed: <env>`. Skip only for doc/config/type-only items.]
|
|
108
114
|
|
|
109
115
|
## Sign-in Required
|
|
110
116
|
[Include this section ONLY if the work touches authenticated surfaces.
|
|
@@ -53,7 +53,7 @@ Resolve build labels from `.lisa.config.json` `github.labels.build.*` (defaults:
|
|
|
53
53
|
|
|
54
54
|
If `github-verify` returns `FAIL` on any of the above, do NOT continue to build. **Draft the missing spec content first, then block for confirmation** — never bounce a raw "go write all this" checklist back to the author:
|
|
55
55
|
|
|
56
|
-
1. **Best-effort autofill (before blocking).** Run
|
|
56
|
+
1. **Best-effort autofill (before blocking).** Run `pre-flight-autofill` for every work item. Preserve a human bare configured key or `Confirmed: <env>`; automation writes `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces the annotation with the bare key or `Confirmed: <env>`. For a legacy bare value, use managed draft markers and current ticket content only; provider edit history is not required. A marker proves automation and requires re-annotation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Resolve one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname; exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. A reported bug environment is an example, not a restriction. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop; never infer from arbitrary branch text, URL paths/query strings, or substrings. Write through `github-write-issue`, never overwrite human prose, then re-run `github-verify`.
|
|
57
57
|
2. Relabel: remove the `claimed` label, add the `blocked` label **and** the `human_needed` marker label. Even after the agent drafted what it could, a pre-flight gate failure bounces the issue back to its author because it still needs a human to **confirm the drafted assumptions** or supply something no agent can invent — real missing credentials, access, or an irreducible product/scoping decision — so the marker tells a human scanning the board which blocked issues are waiting on them. The marker is additive to `blocked`, not a replacement. (See the `config-resolution` rule's "Build markers" for when the marker applies and when it must NOT.)
|
|
58
58
|
```bash
|
|
59
59
|
_read_cfg() { local lv gv; lv=$(jq -r "$1 // empty" .lisa.config.local.json 2>/dev/null); gv=$(jq -r "$1 // empty" .lisa.config.json 2>/dev/null); echo "${lv:-${gv}}"; }
|
|
@@ -152,4 +152,4 @@ Note: `done` may be a string or an env-keyed map (`{ dev, staging, production }`
|
|
|
152
152
|
- Never create or materially edit an issue by calling `gh issue create` / `gh issue edit` directly — always delegate to `github-write-issue` (or, from a vendor-neutral caller, `tracker-write`) so relationships, Gherkin criteria, and metadata gates are enforced.
|
|
153
153
|
- If sign-in credentials are in the issue body, extract and pass them to the flow. If the issue touches an authenticated surface and credentials are missing, that is a Step 2 failure — block and reassign rather than guessing.
|
|
154
154
|
- If the issue has a `## Validation Journey` section, pass it to the verifier agent. The Validation Journey's local-verification step must point at the target backend environment named in the body.
|
|
155
|
-
-
|
|
155
|
+
- The environment handoff for every work item uses the same durable grammar: human bare configured key or `Confirmed: <env>`; automated `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. For legacy bare values, managed draft markers and current ticket content decide provenance; provider edit history is not required. A marker proves automation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Human-confirmed wins, then validated `Inferred:`; otherwise one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname drives the implementation base branch. Exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop. Never infer from arbitrary branch text, URL paths/query strings, or substrings. With no signal, use the remote default and record the applicable assumption without blocking on a non-unique reverse-map. Require any selected mapping and remote branch. A reported bug environment is an example of the all-work-type rule. Non-integration fixes still require the linked forward cherry-pick.
|
|
@@ -48,7 +48,7 @@ Use the `jira-verify` skill to check the ticket against organizational standards
|
|
|
48
48
|
**Gating behavior — this is the one place auto-transitioning is allowed:**
|
|
49
49
|
|
|
50
50
|
If `jira-verify` returns `FAIL` on any of the above, do NOT continue to build. **Draft the missing spec content first, then block for confirmation** — never bounce a raw "go write all this" checklist back to the reporter:
|
|
51
|
-
1. **Best-effort autofill (before blocking).** Run
|
|
51
|
+
1. **Best-effort autofill (before blocking).** Run `pre-flight-autofill` for every work item. Preserve a human bare configured key or `Confirmed: <env>`; automation writes `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces the annotation with the bare key or `Confirmed: <env>`. For a legacy bare value, use managed draft markers and current ticket content only; provider edit history is not required. A marker proves automation and requires re-annotation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Resolve one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname; exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. A reported bug environment is an example, not a restriction. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop; never infer from arbitrary branch text, URL paths/query strings, or substrings. Write through `jira-write-ticket`, never overwrite human prose, then re-run `jira-verify`.
|
|
52
52
|
2. Transition the ticket status to the configured `blocked` status (typically `Blocked` — read from `.lisa.config.json` `jira.workflow.blocked` if present, otherwise the project's standard blocked status). Use `mcp__atlassian__transitionJiraIssue` or equivalent.
|
|
53
53
|
3. **Add the `human_needed` marker label.** Even after the agent drafted what it could, a pre-flight gate failure bounces the ticket back to its reporter because the ticket still needs a human to **confirm the drafted assumptions** or supply something no agent can invent — real missing credentials, access, or an irreducible product/scoping decision. Add the configured label (`jira.labels.human_needed`, default `Human Needed`) to the ticket's `labels` field via `mcp__atlassian__editJiraIssue` — a lightweight metadata update permitted under this same gate exception. This is additive to (not a replacement for) the `blocked` status, so a human scanning the board sees at a glance which blocked tickets are waiting on them. If the label does not exist in the project, record that and proceed — the marker is best-effort. (See the `config-resolution` rule's "Build markers" for when the marker applies and when it must NOT.)
|
|
54
54
|
4. Reassign the ticket to the **Reporter** (the human who filed it — not the Creator field, which may be a bot/integration).
|
|
@@ -132,4 +132,4 @@ Note: `done` may be a string or an env-keyed map (`{ dev, staging, production }`
|
|
|
132
132
|
- Never create or materially edit a ticket by calling MCP write tools directly — always delegate to `jira-write-ticket` so relationships, Gherkin criteria, and metadata gates are enforced
|
|
133
133
|
- If sign-in credentials are in the ticket, extract and pass them to the flow. If the ticket touches an authenticated surface and credentials are missing, that is a Step 2 failure — block and reassign rather than guessing.
|
|
134
134
|
- If the ticket has a Validation Journey section, pass it to the verifier agent. The Validation Journey's local-verification step must point at the target backend environment named in the description (for FE work, that's the deployed backend QA reported against).
|
|
135
|
-
-
|
|
135
|
+
- The environment handoff for every work item uses the same durable grammar: human bare configured key or `Confirmed: <env>`; automated `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. For legacy bare values, managed draft markers and current ticket content decide provenance; provider edit history is not required. A marker proves automation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Human-confirmed wins, then validated `Inferred:`; otherwise one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname drives the implementation base branch. Exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop. Never infer from arbitrary branch text, URL paths/query strings, or substrings. With no signal, use the remote default and record the applicable assumption without blocking on a non-unique reverse-map. Require any selected mapping and remote branch. A reported bug environment is an example of the all-work-type rule. Non-integration fixes still require the linked forward cherry-pick.
|
|
@@ -50,7 +50,7 @@ Use the `linear-verify` skill to check the item against organizational standards
|
|
|
50
50
|
Resolve build labels from `.lisa.config.json` `linear.labels.build.*` (defaults: `status:ready` / `status:in-progress` / `status:code-review`); resolve the `blocked` label from the same section (`linear.labels.build.blocked`, default `status:blocked`) and the `human_needed` marker label from the same section (`linear.labels.build.human_needed`, default `human-needed`).
|
|
51
51
|
|
|
52
52
|
If `linear-verify` returns `FAIL` on any of the above, do NOT continue to build. **Draft the missing spec content first, then block for confirmation** — never bounce a raw "go write all this" checklist back to the creator:
|
|
53
|
-
1. **Best-effort autofill (before blocking).** Run
|
|
53
|
+
1. **Best-effort autofill (before blocking).** Run `pre-flight-autofill` for every work item. Preserve a human bare configured key or `Confirmed: <env>`; automation writes `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces the annotation with the bare key or `Confirmed: <env>`. For a legacy bare value, use managed draft markers and current ticket content only; provider edit history is not required. A marker proves automation and requires re-annotation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Resolve one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname; exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. A reported bug environment is an example, not a restriction. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop; never infer from arbitrary branch text, URL paths/query strings, or substrings. Write through `linear-write-issue`, never overwrite human prose, then re-run `linear-verify`.
|
|
54
54
|
2. Update labels via `lisa-linear-access operation: save-issue`: remove the current build label, add the configured `blocked` label **and** the configured `human_needed` marker label. (Create either label via `create_issue_label` if needed.) Even after the agent drafted what it could, a pre-flight gate failure bounces the item back to its creator because it still needs a human to **confirm the drafted assumptions** or supply something no agent can invent — real missing credentials, access, or an irreducible product/scoping decision — so the marker tells a human scanning the board which blocked items are waiting on them. The marker is additive to `blocked`, not a replacement. (See the `config-resolution` rule's "Build markers" for when the marker applies and when it must NOT.)
|
|
55
55
|
3. Reassign the item to the **Issue creator** (the human who filed it — Linear's `creator` field).
|
|
56
56
|
4. Post the **confirmation comment** from the `pre-flight-autofill` rule via `lisa-linear-access operation: save-comment`, **not** a bare remediation checklist: disclose it is a Claude draft, give one line per drafted section naming the key assumption made, list any remaining human-only item as a specific question with a recommended default, and close with *"review the drafted sections, correct anything wrong, then flip back to Ready and it builds — or reply with corrections."* Prefix with `[{repo}]`.
|
|
@@ -135,4 +135,4 @@ The label transitions ARE the canonical signal. The native `state` field stays a
|
|
|
135
135
|
- Never create or materially edit an item by calling MCP write tools directly — always delegate to `linear-write-issue` so relationships, Gherkin criteria, and metadata gates are enforced. Two explicit exceptions are permitted: (1) the Step 2 pre-flight failure path (when `linear-verify` returns `FAIL`) may call `lisa-linear-access operation: save-issue` and `lisa-linear-access operation: save-comment` directly to set `status:blocked`, add the configured `human_needed` marker label, and reassign to the creator — this narrow exception is already granted by the rule above; (2) the Step 3 triage path may call `lisa-linear-access operation: save-comment` to post triage findings and `lisa-linear-access operation: save-issue` to add the `claude-triaged-{repo}` label — these are lightweight metadata updates that do not create or materially edit ticket content and therefore do not need to route through `linear-write-issue`.
|
|
136
136
|
- If sign-in credentials are in the item, extract and pass them to the flow. If the item touches an authenticated surface and credentials are missing, that is a Step 2 failure — block and reassign rather than guessing.
|
|
137
137
|
- If the item has a Validation Journey section, pass it to the verifier agent. The Validation Journey's local-verification step must point at the target backend environment named in the description.
|
|
138
|
-
-
|
|
138
|
+
- The environment handoff for every work item uses the same durable grammar: human bare configured key or `Confirmed: <env>`; automated `Inferred: <env> — evidence: <title|body|reproduction|hostname>`, `Assumption: <env> — remote default branch <branch>` for a unique reverse-map, or `Assumption: remote default branch <branch>` otherwise. Human confirmation replaces an automated annotation with the bare key or `Confirmed: <env>`. For legacy bare values, managed draft markers and current ticket content decide provenance; provider edit history is not required. A marker proves automation; otherwise unknown provenance plus conflicting evidence stops for confirmation. Human-confirmed wins, then validated `Inferred:`; otherwise one exact `deploy.branches` key from the human-authored title, body, and reproduction steps or URL hostname drives the implementation base branch. Exclude the complete `Target Backend Environment` section and other machine-authored metadata/draft blocks so annotations cannot become evidence. Evidence supersedes only `Assumption:`. Normalize `prod` ↔ `production` only when exactly one is configured; no other aliases exist. Conflicts stop. Never infer from arbitrary branch text, URL paths/query strings, or substrings. With no signal, use the remote default and record the applicable assumption without blocking on a non-unique reverse-map. Require any selected mapping and remote branch. A reported bug environment is an example of the all-work-type rule. Non-integration fixes still require the linked forward cherry-pick.
|
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
createAutomationGroupBins,
|
|
16
16
|
renderAutomationGroups,
|
|
17
17
|
} from "./automation-status-expected-fleet.mjs";
|
|
18
|
+
import {
|
|
19
|
+
resolveAutomationRunDisplay,
|
|
20
|
+
resolveRecoveryEscalation,
|
|
21
|
+
} from "./automation-status-run-history.mjs";
|
|
18
22
|
|
|
19
23
|
const CLAUDE_RUNTIME_LABEL = "Claude /schedule";
|
|
20
24
|
const CLAUDE_ACTIVE_STATUSES = new Set([
|
|
@@ -51,9 +55,10 @@ const NEGATED_FAILURE_PATTERN =
|
|
|
51
55
|
* @param {{
|
|
52
56
|
* readonly expectedFleet: ExpectedFleet
|
|
53
57
|
* readonly scheduleListing?: string | readonly unknown[] | Record<string, unknown> | null
|
|
58
|
+
* readonly projectRoot?: string
|
|
54
59
|
* readonly now?: string | Date
|
|
55
60
|
* }} input
|
|
56
|
-
* @returns {{
|
|
61
|
+
* @returns {Promise<{
|
|
57
62
|
* readonly runtime: string
|
|
58
63
|
* readonly generatedAt: string
|
|
59
64
|
* readonly groups: readonly {
|
|
@@ -66,20 +71,27 @@ const NEGATED_FAILURE_PATTERN =
|
|
|
66
71
|
* readonly expectedCadence?: string
|
|
67
72
|
* readonly expectedCommand?: string
|
|
68
73
|
* readonly observed?: string
|
|
74
|
+
* readonly runbook?: string
|
|
75
|
+
* readonly lastOutcome?: { readonly ts: string, readonly outcome: string, readonly summary: string }
|
|
76
|
+
* readonly outcomeHistory?: readonly string[]
|
|
77
|
+
* readonly olderRecordCount?: number
|
|
69
78
|
* readonly remediation?: string
|
|
70
79
|
* }[]
|
|
71
80
|
* }[]
|
|
72
81
|
* readonly observedAutomations: readonly ObservedClaudeAutomation[]
|
|
73
|
-
* }}
|
|
82
|
+
* }>}
|
|
74
83
|
*/
|
|
75
|
-
export function inspectClaudeAutomationFleet(input) {
|
|
84
|
+
export async function inspectClaudeAutomationFleet(input) {
|
|
76
85
|
const expectedFleet = input.expectedFleet;
|
|
77
86
|
const now = normalizeDate(input.now);
|
|
87
|
+
const projectRoot = input.projectRoot ?? process.cwd();
|
|
78
88
|
const observedAutomations = listClaudeAutomations({
|
|
79
89
|
scheduleListing: input.scheduleListing,
|
|
80
90
|
automationPrefix: expectedFleet.automationPrefix,
|
|
81
91
|
});
|
|
82
92
|
|
|
93
|
+
const runDisplays = await resolveFleetRunDisplays(projectRoot, expectedFleet);
|
|
94
|
+
|
|
83
95
|
const expectedGroups = createAutomationGroupBins();
|
|
84
96
|
|
|
85
97
|
const comparisons = compareAutomationFleet({
|
|
@@ -96,17 +108,23 @@ export function inspectClaudeAutomationFleet(input) {
|
|
|
96
108
|
expected,
|
|
97
109
|
comparison,
|
|
98
110
|
now,
|
|
111
|
+
runDisplay: runDisplays.get(expected.id),
|
|
99
112
|
})
|
|
100
113
|
);
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
for (const unsupported of expectedFleet.unsupported) {
|
|
117
|
+
const runDisplay = runDisplays.get(unsupported.id);
|
|
104
118
|
assignToAutomationGroup(expectedGroups, unsupported.group, {
|
|
105
119
|
id: unsupported.automationId,
|
|
106
120
|
status: "UNSUPPORTED",
|
|
107
121
|
summary: unsupported.reason,
|
|
108
122
|
expectedCadence: unsupported.expectedCadence,
|
|
109
123
|
observed: "No automation is expected for this repo/runtime combination.",
|
|
124
|
+
runbook: runDisplay?.runbook,
|
|
125
|
+
lastOutcome: runDisplay?.lastOutcome,
|
|
126
|
+
outcomeHistory: runDisplay?.outcomeHistory,
|
|
127
|
+
olderRecordCount: runDisplay?.olderRecordCount,
|
|
110
128
|
});
|
|
111
129
|
}
|
|
112
130
|
|
|
@@ -195,15 +213,43 @@ function extractClaudeScheduleCadence(command) {
|
|
|
195
213
|
);
|
|
196
214
|
}
|
|
197
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Read the read-only run-history display for every expected and unsupported
|
|
218
|
+
* loop up front, keyed by the loop's short id, so the per-entry item builder
|
|
219
|
+
* stays synchronous.
|
|
220
|
+
*
|
|
221
|
+
* @param {string} projectRoot
|
|
222
|
+
* @param {ExpectedFleet} expectedFleet
|
|
223
|
+
* @returns {Promise<Map<string, import("./automation-status-run-history.mjs").AutomationRunDisplay>>}
|
|
224
|
+
*/
|
|
225
|
+
async function resolveFleetRunDisplays(projectRoot, expectedFleet) {
|
|
226
|
+
const entries = [...expectedFleet.expected, ...expectedFleet.unsupported];
|
|
227
|
+
const displays = await Promise.all(
|
|
228
|
+
entries.map(entry =>
|
|
229
|
+
resolveAutomationRunDisplay({
|
|
230
|
+
projectRoot,
|
|
231
|
+
loopId: entry.id,
|
|
232
|
+
runbookPath: entry.runbookPath,
|
|
233
|
+
})
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
return new Map(entries.map((entry, index) => [entry.id, displays[index]]));
|
|
237
|
+
}
|
|
238
|
+
|
|
198
239
|
function createObservedStatusItem(input) {
|
|
199
240
|
const expected = input.expected;
|
|
200
241
|
const comparison = input.comparison;
|
|
201
242
|
const observed = comparison.observedAutomation;
|
|
243
|
+
const runDisplay = input.runDisplay;
|
|
202
244
|
const runSignal = classifyAutomationRunSignal({
|
|
203
245
|
expected,
|
|
204
246
|
observedAutomation: observed,
|
|
205
247
|
now: input.now,
|
|
206
248
|
});
|
|
249
|
+
// Three or more consecutive recorded recovery-required runs flip the loop to
|
|
250
|
+
// FAILING even when the /schedule entry looks fine — the fleet-health signal
|
|
251
|
+
// the scheduler surface cannot see. It wins over the scheduler run-signal.
|
|
252
|
+
const escalation = resolveRecoveryEscalation(runDisplay);
|
|
207
253
|
|
|
208
254
|
const observedDetails = [comparison.observed];
|
|
209
255
|
if (observed?.status) {
|
|
@@ -223,20 +269,30 @@ function createObservedStatusItem(input) {
|
|
|
223
269
|
}
|
|
224
270
|
|
|
225
271
|
const status =
|
|
272
|
+
escalation?.status ??
|
|
226
273
|
runSignal?.status ??
|
|
227
274
|
/** @type {"HEALTHY" | "MISSING" | "DRIFTED"} */ (comparison.status);
|
|
228
275
|
|
|
229
276
|
return {
|
|
230
277
|
id: expected.automationId,
|
|
231
278
|
status,
|
|
232
|
-
summary:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
279
|
+
summary: escalation
|
|
280
|
+
? escalation.summary
|
|
281
|
+
: composeAutomationSummary({
|
|
282
|
+
comparison,
|
|
283
|
+
runSignal,
|
|
284
|
+
}),
|
|
236
285
|
expectedCadence: expected.expectedCadence,
|
|
237
286
|
expectedCommand: expected.expectedCommand,
|
|
238
287
|
observed: observedDetails.join(" "),
|
|
239
|
-
|
|
288
|
+
runbook: runDisplay?.runbook,
|
|
289
|
+
lastOutcome: runDisplay?.lastOutcome,
|
|
290
|
+
outcomeHistory: runDisplay?.outcomeHistory,
|
|
291
|
+
olderRecordCount: runDisplay?.olderRecordCount,
|
|
292
|
+
remediation:
|
|
293
|
+
escalation?.remediation ??
|
|
294
|
+
runSignal?.remediation ??
|
|
295
|
+
comparison.remediation,
|
|
240
296
|
};
|
|
241
297
|
}
|
|
242
298
|
|
|
@@ -22,6 +22,10 @@ import {
|
|
|
22
22
|
createAutomationGroupBins,
|
|
23
23
|
renderAutomationGroups,
|
|
24
24
|
} from "./automation-status-expected-fleet.mjs";
|
|
25
|
+
import {
|
|
26
|
+
resolveAutomationRunDisplay,
|
|
27
|
+
resolveRecoveryEscalation,
|
|
28
|
+
} from "./automation-status-run-history.mjs";
|
|
25
29
|
|
|
26
30
|
const CODEx_RUNTIME_LABEL = "Codex automations";
|
|
27
31
|
const RUN_TIMESTAMP_PATTERN = /20\d{2}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?Z/;
|
|
@@ -132,6 +136,7 @@ async function execGitWithRetry(args, options, attempts = 4) {
|
|
|
132
136
|
* @param {{
|
|
133
137
|
* readonly expectedFleet: ExpectedFleet
|
|
134
138
|
* readonly automationsDir?: string
|
|
139
|
+
* readonly projectRoot?: string
|
|
135
140
|
* readonly now?: string | Date
|
|
136
141
|
* }} input
|
|
137
142
|
* @returns {Promise<{
|
|
@@ -147,6 +152,10 @@ async function execGitWithRetry(args, options, attempts = 4) {
|
|
|
147
152
|
* readonly expectedCadence?: string
|
|
148
153
|
* readonly expectedCommand?: string
|
|
149
154
|
* readonly observed?: string
|
|
155
|
+
* readonly runbook?: string
|
|
156
|
+
* readonly lastOutcome?: { readonly ts: string, readonly outcome: string, readonly summary: string }
|
|
157
|
+
* readonly outcomeHistory?: readonly string[]
|
|
158
|
+
* readonly olderRecordCount?: number
|
|
150
159
|
* readonly remediation?: string
|
|
151
160
|
* }[]
|
|
152
161
|
* }[]
|
|
@@ -156,11 +165,14 @@ async function execGitWithRetry(args, options, attempts = 4) {
|
|
|
156
165
|
export async function inspectCodexAutomationFleet(input) {
|
|
157
166
|
const expectedFleet = input.expectedFleet;
|
|
158
167
|
const now = normalizeDate(input.now);
|
|
168
|
+
const projectRoot = input.projectRoot ?? process.cwd();
|
|
159
169
|
const observedAutomations = await listCodexAutomations({
|
|
160
170
|
automationsDir: input.automationsDir,
|
|
161
171
|
automationPrefix: expectedFleet.automationPrefix,
|
|
162
172
|
});
|
|
163
173
|
|
|
174
|
+
const runDisplays = await resolveFleetRunDisplays(projectRoot, expectedFleet);
|
|
175
|
+
|
|
164
176
|
const expectedGroups = createAutomationGroupBins();
|
|
165
177
|
|
|
166
178
|
const comparisons = compareAutomationFleet({
|
|
@@ -177,17 +189,23 @@ export async function inspectCodexAutomationFleet(input) {
|
|
|
177
189
|
expected,
|
|
178
190
|
comparison,
|
|
179
191
|
now,
|
|
192
|
+
runDisplay: runDisplays.get(expected.id),
|
|
180
193
|
})
|
|
181
194
|
);
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
for (const unsupported of expectedFleet.unsupported) {
|
|
198
|
+
const runDisplay = runDisplays.get(unsupported.id);
|
|
185
199
|
assignToAutomationGroup(expectedGroups, unsupported.group, {
|
|
186
200
|
id: unsupported.automationId,
|
|
187
201
|
status: "UNSUPPORTED",
|
|
188
202
|
summary: unsupported.reason,
|
|
189
203
|
expectedCadence: unsupported.expectedCadence,
|
|
190
204
|
observed: "No automation is expected for this repo/runtime combination.",
|
|
205
|
+
runbook: runDisplay?.runbook,
|
|
206
|
+
lastOutcome: runDisplay?.lastOutcome,
|
|
207
|
+
outcomeHistory: runDisplay?.outcomeHistory,
|
|
208
|
+
olderRecordCount: runDisplay?.olderRecordCount,
|
|
191
209
|
});
|
|
192
210
|
}
|
|
193
211
|
|
|
@@ -374,15 +392,43 @@ async function readCodexAutomation(automationDir) {
|
|
|
374
392
|
};
|
|
375
393
|
}
|
|
376
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Read the read-only run-history display for every expected and unsupported
|
|
397
|
+
* loop up front, keyed by the loop's short id, so the per-entry item builder
|
|
398
|
+
* stays synchronous.
|
|
399
|
+
*
|
|
400
|
+
* @param {string} projectRoot
|
|
401
|
+
* @param {ExpectedFleet} expectedFleet
|
|
402
|
+
* @returns {Promise<Map<string, import("./automation-status-run-history.mjs").AutomationRunDisplay>>}
|
|
403
|
+
*/
|
|
404
|
+
async function resolveFleetRunDisplays(projectRoot, expectedFleet) {
|
|
405
|
+
const entries = [...expectedFleet.expected, ...expectedFleet.unsupported];
|
|
406
|
+
const displays = await Promise.all(
|
|
407
|
+
entries.map(entry =>
|
|
408
|
+
resolveAutomationRunDisplay({
|
|
409
|
+
projectRoot,
|
|
410
|
+
loopId: entry.id,
|
|
411
|
+
runbookPath: entry.runbookPath,
|
|
412
|
+
})
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
return new Map(entries.map((entry, index) => [entry.id, displays[index]]));
|
|
416
|
+
}
|
|
417
|
+
|
|
377
418
|
function createObservedStatusItem(input) {
|
|
378
419
|
const expected = input.expected;
|
|
379
420
|
const comparison = input.comparison;
|
|
380
421
|
const observed = comparison.observedAutomation;
|
|
422
|
+
const runDisplay = input.runDisplay;
|
|
381
423
|
const runSignal = classifyAutomationRunSignal({
|
|
382
424
|
expected,
|
|
383
425
|
observedAutomation: observed,
|
|
384
426
|
now: input.now,
|
|
385
427
|
});
|
|
428
|
+
// Three or more consecutive recorded recovery-required runs flip the loop to
|
|
429
|
+
// FAILING even when the scheduler entry looks fine — the fleet-health signal
|
|
430
|
+
// the raw backing store cannot see. It wins over the scheduler run-signal.
|
|
431
|
+
const escalation = resolveRecoveryEscalation(runDisplay);
|
|
386
432
|
|
|
387
433
|
const observedDetails = [comparison.observed];
|
|
388
434
|
if (observed?.status) {
|
|
@@ -401,20 +447,30 @@ function createObservedStatusItem(input) {
|
|
|
401
447
|
}
|
|
402
448
|
|
|
403
449
|
const status =
|
|
450
|
+
escalation?.status ??
|
|
404
451
|
runSignal?.status ??
|
|
405
452
|
/** @type {"HEALTHY" | "MISSING" | "DRIFTED"} */ (comparison.status);
|
|
406
453
|
|
|
407
454
|
return {
|
|
408
455
|
id: expected.automationId,
|
|
409
456
|
status,
|
|
410
|
-
summary:
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
457
|
+
summary: escalation
|
|
458
|
+
? escalation.summary
|
|
459
|
+
: composeAutomationSummary({
|
|
460
|
+
comparison,
|
|
461
|
+
runSignal,
|
|
462
|
+
}),
|
|
414
463
|
expectedCadence: expected.expectedCadence,
|
|
415
464
|
expectedCommand: expected.expectedCommand,
|
|
416
465
|
observed: observedDetails.join(" "),
|
|
417
|
-
|
|
466
|
+
runbook: runDisplay?.runbook,
|
|
467
|
+
lastOutcome: runDisplay?.lastOutcome,
|
|
468
|
+
outcomeHistory: runDisplay?.outcomeHistory,
|
|
469
|
+
olderRecordCount: runDisplay?.olderRecordCount,
|
|
470
|
+
remediation:
|
|
471
|
+
escalation?.remediation ??
|
|
472
|
+
runSignal?.remediation ??
|
|
473
|
+
comparison.remediation,
|
|
418
474
|
};
|
|
419
475
|
}
|
|
420
476
|
|
|
@@ -26,12 +26,22 @@ export const AUTOMATION_FLEET_VERDICTS = [
|
|
|
26
26
|
* @typedef {"HEALTHY" | "PARTIAL_SUPPORT" | "ATTENTION_NEEDED"} AutomationFleetVerdict
|
|
27
27
|
*
|
|
28
28
|
* @typedef {{
|
|
29
|
+
* readonly ts: string
|
|
30
|
+
* readonly outcome: string
|
|
31
|
+
* readonly summary: string
|
|
32
|
+
* }} AutomationLastOutcome
|
|
33
|
+
*
|
|
34
|
+
* @typedef {{
|
|
29
35
|
* readonly id: string
|
|
30
36
|
* readonly status: AutomationHealthStatus
|
|
31
37
|
* readonly summary: string
|
|
32
38
|
* readonly expectedCadence?: string
|
|
33
39
|
* readonly expectedCommand?: string
|
|
34
40
|
* readonly observed?: string
|
|
41
|
+
* readonly runbook?: string
|
|
42
|
+
* readonly lastOutcome?: AutomationLastOutcome
|
|
43
|
+
* readonly outcomeHistory?: readonly string[]
|
|
44
|
+
* readonly olderRecordCount?: number
|
|
35
45
|
* readonly remediation?: string
|
|
36
46
|
* }} AutomationStatusItem
|
|
37
47
|
*
|
|
@@ -130,6 +140,26 @@ export function renderAutomationStatusReport(input) {
|
|
|
130
140
|
if (item.observed) {
|
|
131
141
|
lines.push(` Observed: ${item.observed}`);
|
|
132
142
|
}
|
|
143
|
+
// Contract + run-history block: observable facts about where the runbook
|
|
144
|
+
// lives and how recent runs ended, kept between Observed and Remediation
|
|
145
|
+
// so operators read the state before the fix. Every line degrades
|
|
146
|
+
// explicitly — an absent runbook or empty history is stated, never blank.
|
|
147
|
+
if (item.runbook !== undefined) {
|
|
148
|
+
lines.push(` Runbook: ${item.runbook}`);
|
|
149
|
+
lines.push(
|
|
150
|
+
item.lastOutcome
|
|
151
|
+
? ` Last run: ${item.lastOutcome.outcome} — ${item.lastOutcome.summary} (${item.lastOutcome.ts})`
|
|
152
|
+
: " Last run: no recorded runs yet"
|
|
153
|
+
);
|
|
154
|
+
if (item.outcomeHistory && item.outcomeHistory.length > 0) {
|
|
155
|
+
const historyLine = ` History: ${item.outcomeHistory.join(", ")} (newest first)`;
|
|
156
|
+
lines.push(
|
|
157
|
+
item.olderRecordCount && item.olderRecordCount > 0
|
|
158
|
+
? `${historyLine}; … and ${item.olderRecordCount} older records`
|
|
159
|
+
: historyLine
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
133
163
|
if (item.remediation) {
|
|
134
164
|
lines.push(` Remediation: ${item.remediation}`);
|
|
135
165
|
}
|