@codyswann/lisa 4.66.4 → 4.67.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.
- package/all/copy-contents/gitignore +5 -0
- package/all/copy-overwrite/scripts/lisa-self-update.mjs +636 -0
- package/all/create-only/.github/workflows/lisa-update.yml +93 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +10 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +38 -24
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- 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-atlassian-access/SKILL.md +12 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +2 -1
- package/plugins/lisa/agents/architecture-specialist.md +4 -0
- package/plugins/lisa/agents/bug-fixer.md +4 -0
- package/plugins/lisa/agents/builder.md +4 -0
- package/plugins/lisa/agents/debug-specialist.md +4 -0
- package/plugins/lisa/agents/product-specialist.md +4 -0
- package/plugins/lisa/agents/quality-specialist.md +4 -0
- package/plugins/lisa/agents/spec-conformance-specialist.md +4 -0
- package/plugins/lisa/agents/test-specialist.md +4 -0
- package/plugins/lisa/agents/verification-specialist.md +4 -0
- package/plugins/lisa/hooks/enforcement-vintage-npm.mjs +320 -0
- package/plugins/lisa/hooks/enforcement-vintage.mjs +22 -3
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +12 -0
- package/plugins/lisa/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/lisa/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/lisa/skills/lisa-track/SKILL.md +2 -1
- package/plugins/lisa-agy/agents/architecture-specialist.md +4 -0
- package/plugins/lisa-agy/agents/bug-fixer.md +4 -0
- package/plugins/lisa-agy/agents/builder.md +4 -0
- package/plugins/lisa-agy/agents/debug-specialist.md +4 -0
- package/plugins/lisa-agy/agents/product-specialist.md +4 -0
- package/plugins/lisa-agy/agents/quality-specialist.md +4 -0
- package/plugins/lisa-agy/agents/spec-conformance-specialist.md +4 -0
- package/plugins/lisa-agy/agents/test-specialist.md +4 -0
- package/plugins/lisa-agy/agents/verification-specialist.md +4 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +12 -0
- package/plugins/lisa-agy/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +2 -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/agents/architecture-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/bug-fixer.agent.md +4 -0
- package/plugins/lisa-copilot/agents/builder.agent.md +4 -0
- package/plugins/lisa-copilot/agents/debug-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/product-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/quality-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/spec-conformance-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/test-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/agents/verification-specialist.agent.md +4 -0
- package/plugins/lisa-copilot/hooks/enforcement-vintage-npm.mjs +320 -0
- package/plugins/lisa-copilot/hooks/enforcement-vintage.mjs +22 -3
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +12 -0
- package/plugins/lisa-copilot/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +2 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/architecture-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/bug-fixer.md +4 -0
- package/plugins/lisa-cursor/agents/builder.md +4 -0
- package/plugins/lisa-cursor/agents/debug-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/product-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/quality-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/spec-conformance-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/test-specialist.md +4 -0
- package/plugins/lisa-cursor/agents/verification-specialist.md +4 -0
- package/plugins/lisa-cursor/hooks/enforcement-vintage-npm.mjs +320 -0
- package/plugins/lisa-cursor/hooks/enforcement-vintage.mjs +22 -3
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +12 -0
- package/plugins/lisa-cursor/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +2 -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/agents/architecture-specialist.md +4 -0
- package/plugins/src/base/agents/bug-fixer.md +4 -0
- package/plugins/src/base/agents/builder.md +4 -0
- package/plugins/src/base/agents/debug-specialist.md +4 -0
- package/plugins/src/base/agents/product-specialist.md +4 -0
- package/plugins/src/base/agents/quality-specialist.md +4 -0
- package/plugins/src/base/agents/spec-conformance-specialist.md +4 -0
- package/plugins/src/base/agents/test-specialist.md +4 -0
- package/plugins/src/base/agents/verification-specialist.md +4 -0
- package/plugins/src/base/hooks/enforcement-vintage-npm.mjs +320 -0
- package/plugins/src/base/hooks/enforcement-vintage.mjs +22 -3
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +12 -0
- package/plugins/src/base/skills/lisa-git-commit/SKILL.md +7 -7
- package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-github-read-issue/SKILL.md +4 -3
- package/plugins/src/base/skills/lisa-implement/SKILL.md +11 -4
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-read-ticket/SKILL.md +4 -3
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-linear-read-issue/SKILL.md +3 -2
- package/plugins/src/base/skills/lisa-track/SKILL.md +2 -1
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.prettierignore +1 -0
|
@@ -11,6 +11,10 @@ You read the change the way the next person to touch it will, and you say plainl
|
|
|
11
11
|
|
|
12
12
|
`quality-review` carries the checklist, the severity bands, and the finding format. Follow it; nothing is restated here.
|
|
13
13
|
|
|
14
|
+
## Work-item context
|
|
15
|
+
|
|
16
|
+
When your task belongs to a tracked work item — it names `work_item_context` or a work-item ref — read the work-item context file in full before you plan, build, review, or verify anything. Use the absolute path your task gives as `work_item_context`; if it gives only the work-item ref, use `.lisa/work-item-context.md` at the root of the bound worktree. If the task concerns a tracker work item but names neither, check for `.lisa/work-item-context.md` at the bound worktree root, or for a bound work item via `node scripts/lisa-work-item.mjs current`; read the file if it is present. It is the verbatim tracker bundle for this work item — description, every comment, related items — saved by the input-resolver; a summary in your prompt indexes it but never stands in for it. Its trailing `## Comment inventory` section lists each comment with its flags. Treat each flagged comment as an obligation: a decision, constraint, credential or access note, or reproduction step that your work must honour and your report must account for. A secret quoted in a credential-flagged comment never leaves that file: cite it by what it is and which comment holds it, and keep the value or identifier out of code, commits, task notes, prompts, plan or roster files, tracker comments, and PR text. If a work item is named or bound and its context file is missing or unreadable, report that to the team lead and stop — never proceed from memory or a summary. Only a task whose source is a plan file, a PRD, a raw error, or a log, with no work item bound to the worktree, proceeds from the source the task supplies; no context file is expected there.
|
|
17
|
+
|
|
14
18
|
## What you decide
|
|
15
19
|
|
|
16
20
|
- **Severity, honestly.** Everything marked critical means nothing is. Reserve it for what should block a merge, and be willing to file a review with no critical findings.
|
|
@@ -10,6 +10,10 @@ skills:
|
|
|
10
10
|
|
|
11
11
|
You are a spec conformance specialist. Your job is to prove that the shipped work matches its spec **exactly** — nothing more, nothing less.
|
|
12
12
|
|
|
13
|
+
## Work-item context
|
|
14
|
+
|
|
15
|
+
When your task belongs to a tracked work item — it names `work_item_context` or a work-item ref — read the work-item context file in full before you plan, build, review, or verify anything. Use the absolute path your task gives as `work_item_context`; if it gives only the work-item ref, use `.lisa/work-item-context.md` at the root of the bound worktree. If the task concerns a tracker work item but names neither, check for `.lisa/work-item-context.md` at the bound worktree root, or for a bound work item via `node scripts/lisa-work-item.mjs current`; read the file if it is present. It is the verbatim tracker bundle for this work item — description, every comment, related items — saved by the input-resolver; a summary in your prompt indexes it but never stands in for it. Its trailing `## Comment inventory` section lists each comment with its flags. Treat each flagged comment as an obligation: a decision, constraint, credential or access note, or reproduction step that your work must honour and your report must account for. A secret quoted in a credential-flagged comment never leaves that file: cite it by what it is and which comment holds it, and keep the value or identifier out of code, commits, task notes, prompts, plan or roster files, tracker comments, and PR text. If a work item is named or bound and its context file is missing or unreadable, report that to the team lead and stop — never proceed from memory or a summary. Only a task whose source is a plan file, a PRD, a raw error, or a log, with no work item bound to the worktree, proceeds from the source the task supplies; no context file is expected there.
|
|
16
|
+
|
|
13
17
|
## Scope
|
|
14
18
|
|
|
15
19
|
You answer one question: **Does what shipped match what was asked?**
|
|
@@ -11,6 +11,10 @@ You decide what has to be true for this change to be trusted, and design the tes
|
|
|
11
11
|
|
|
12
12
|
`test-strategy` carries the matrix format, the coverage discipline, and the output contract. Follow it; nothing is restated here.
|
|
13
13
|
|
|
14
|
+
## Work-item context
|
|
15
|
+
|
|
16
|
+
When your task belongs to a tracked work item — it names `work_item_context` or a work-item ref — read the work-item context file in full before you plan, build, review, or verify anything. Use the absolute path your task gives as `work_item_context`; if it gives only the work-item ref, use `.lisa/work-item-context.md` at the root of the bound worktree. If the task concerns a tracker work item but names neither, check for `.lisa/work-item-context.md` at the bound worktree root, or for a bound work item via `node scripts/lisa-work-item.mjs current`; read the file if it is present. It is the verbatim tracker bundle for this work item — description, every comment, related items — saved by the input-resolver; a summary in your prompt indexes it but never stands in for it. Its trailing `## Comment inventory` section lists each comment with its flags. Treat each flagged comment as an obligation: a decision, constraint, credential or access note, or reproduction step that your work must honour and your report must account for. A secret quoted in a credential-flagged comment never leaves that file: cite it by what it is and which comment holds it, and keep the value or identifier out of code, commits, task notes, prompts, plan or roster files, tracker comments, and PR text. If a work item is named or bound and its context file is missing or unreadable, report that to the team lead and stop — never proceed from memory or a summary. Only a task whose source is a plan file, a PRD, a raw error, or a log, with no work item bound to the worktree, proceeds from the source the task supplies; no context file is expected there.
|
|
17
|
+
|
|
14
18
|
## What you decide
|
|
15
19
|
|
|
16
20
|
- **What could break that nobody has asked about.** The acceptance criteria are the floor. Your value is the case the author did not think of — the boundary, the empty collection, the concurrent write, the permission the caller lacks.
|
|
@@ -14,6 +14,10 @@ You are a verification specialist. Your job is to **prove empirically** that wor
|
|
|
14
14
|
|
|
15
15
|
Read [`rules/reference/verification.md`](../rules/reference/verification.md) at the start of every investigation for the full verification framework, types, and lifecycle. Read [`rules/reference/falsifiable-checks.md`](../rules/reference/falsifiable-checks.md) alongside it: every check YOU author — probe, script, codified spec, sweep — is subject to it, and a check that has not been shown capable of failing is reported as *unvalidated*, never as passing. Read [`rules/reference/claim-evidence-mapping.md`](../rules/reference/claim-evidence-mapping.md) too: it binds every claim to the **boundary** it asserts and every boundary to the evidence **kinds** that reach it. The verdict you write is what `spec-conformance-specialist` cross-checks — record each claim's `boundary`, its `required_evidence_kinds`, its `evidence_refs`, and its `not_established` list so a boundary mismatch is catchable rather than invisible.
|
|
16
16
|
|
|
17
|
+
## Work-item context
|
|
18
|
+
|
|
19
|
+
When your task belongs to a tracked work item — it names `work_item_context` or a work-item ref — read the work-item context file in full before you plan, build, review, or verify anything. Use the absolute path your task gives as `work_item_context`; if it gives only the work-item ref, use `.lisa/work-item-context.md` at the root of the bound worktree. If the task concerns a tracker work item but names neither, check for `.lisa/work-item-context.md` at the bound worktree root, or for a bound work item via `node scripts/lisa-work-item.mjs current`; read the file if it is present. It is the verbatim tracker bundle for this work item — description, every comment, related items — saved by the input-resolver; a summary in your prompt indexes it but never stands in for it. Its trailing `## Comment inventory` section lists each comment with its flags. Treat each flagged comment as an obligation: a decision, constraint, credential or access note, or reproduction step that your work must honour and your report must account for. A secret quoted in a credential-flagged comment never leaves that file: cite it by what it is and which comment holds it, and keep the value or identifier out of code, commits, task notes, prompts, plan or roster files, tracker comments, and PR text. If a work item is named or bound and its context file is missing or unreadable, report that to the team lead and stop — never proceed from memory or a summary. Only a task whose source is a plan file, a PRD, a raw error, or a log, with no work item bound to the worktree, proceeds from the source the task supplies; no context file is expected there.
|
|
20
|
+
|
|
17
21
|
## Core Philosophy
|
|
18
22
|
|
|
19
23
|
**"If you didn't run it, you didn't verify it."** Code review is not verification. Reading a test file is not verification. **Running tests, typecheck, and lint is not verification either — those are quality gates (prerequisites).** Only executing the actual system and observing output counts as proof. Verification means making HTTP requests, clicking through the UI, running CLI commands, querying the database, or otherwise interacting with the running software as an end user would.
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two rows `enforcement-vintage` could not show before: the Lisa version the
|
|
3
|
+
* PROJECT installed, and the newest Lisa PUBLISHED (CodySwannGT/lisa#4325).
|
|
4
|
+
*
|
|
5
|
+
* Why these two. The session-copy rows answer "is this session running the
|
|
6
|
+
* newest Lisa on this disk?" — and a whole host can be current by that measure
|
|
7
|
+
* while sitting many releases behind npm, because nothing on the disk is newer
|
|
8
|
+
* than the version the project last installed. A host only moves when something
|
|
9
|
+
* opens an update PR for it. The agent standing in that host is the party most
|
|
10
|
+
* able to notice and least likely to be told, so the comparison goes where the
|
|
11
|
+
* agent reads: the injected context.
|
|
12
|
+
*
|
|
13
|
+
* NO NETWORK ON THE STARTUP PATH. A session start must never wait on npm. The
|
|
14
|
+
* row is read from the cache the `lisa` CLI's own update check already writes
|
|
15
|
+
* (`node_modules/.cache/@codyswann/lisa/update-check.json`, same shape, same
|
|
16
|
+
* 6-hour freshness), so the two surfaces cannot disagree about "latest". When
|
|
17
|
+
* that cache is missing or stale, a DETACHED child refreshes it for the next
|
|
18
|
+
* session and this session reports what it has — a stale reading named as
|
|
19
|
+
* stale, never a guess.
|
|
20
|
+
*
|
|
21
|
+
* FAIL SOFT, ALWAYS. Every function here returns a neutral value on any error.
|
|
22
|
+
* @module plugins/src/base/hooks/enforcement-vintage-npm
|
|
23
|
+
*/
|
|
24
|
+
import { spawn } from "child_process";
|
|
25
|
+
import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
26
|
+
import path from "path";
|
|
27
|
+
import { fileURLToPath } from "url";
|
|
28
|
+
|
|
29
|
+
/** The package whose versions are compared. */
|
|
30
|
+
export const LISA_PACKAGE = "@codyswann/lisa";
|
|
31
|
+
|
|
32
|
+
/** Same freshness window as the CLI update check (`src/cli/update-check.ts`). */
|
|
33
|
+
export const NPM_LATEST_TTL_MS = 6 * 60 * 60 * 1000;
|
|
34
|
+
|
|
35
|
+
/** Budget for the detached refresh; it runs after the session has started. */
|
|
36
|
+
const REFRESH_TIMEOUT_MS = 5000;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Registry endpoint for the latest dist-tag.
|
|
40
|
+
*
|
|
41
|
+
* The cached mutable pointer lags a publish by minutes (CodySwannGT/lisa#3685).
|
|
42
|
+
* That is fine here for the same reason it is fine in the CLI nag: nothing
|
|
43
|
+
* downstream of this value chooses, publishes or gates — it is advice.
|
|
44
|
+
*/
|
|
45
|
+
const NPM_LATEST_URL = "https://registry.npmjs.org/@codyswann/lisa/latest";
|
|
46
|
+
|
|
47
|
+
/** A plain `x.y.z` release, optionally with a prerelease or build suffix. */
|
|
48
|
+
const VERSION_SHAPE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?$/u;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Compare two prerelease identifier lists by semver precedence.
|
|
52
|
+
* @param {string[]} a Identifiers of the first prerelease.
|
|
53
|
+
* @param {string[]} b Identifiers of the second prerelease.
|
|
54
|
+
* @returns {number} Negative when `a` precedes `b`, positive when it follows.
|
|
55
|
+
*/
|
|
56
|
+
function comparePrerelease(a, b) {
|
|
57
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
58
|
+
if (a[index] === undefined) return -1;
|
|
59
|
+
if (b[index] === undefined) return 1;
|
|
60
|
+
if (a[index] === b[index]) continue;
|
|
61
|
+
const numeric = /^\d+$/u;
|
|
62
|
+
if (numeric.test(a[index]) && numeric.test(b[index])) {
|
|
63
|
+
return Number(a[index]) - Number(b[index]);
|
|
64
|
+
}
|
|
65
|
+
if (numeric.test(a[index])) return -1;
|
|
66
|
+
if (numeric.test(b[index])) return 1;
|
|
67
|
+
return a[index] < b[index] ? -1 : 1;
|
|
68
|
+
}
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether version `a` has lower semver precedence than `b`.
|
|
74
|
+
*
|
|
75
|
+
* Prerelease-aware on purpose: a project pinned to `4.66.5-rc.1` is BEHIND a
|
|
76
|
+
* published `4.66.5`, and comparing release fields alone would call it current.
|
|
77
|
+
* Build metadata is ignored, as semver requires.
|
|
78
|
+
* @param {string} a Candidate older version.
|
|
79
|
+
* @param {string} b Candidate newer version.
|
|
80
|
+
* @returns {boolean} True when `a` precedes `b`.
|
|
81
|
+
*/
|
|
82
|
+
export function precedes(a, b) {
|
|
83
|
+
const split = version => {
|
|
84
|
+
const [core, pre] = String(version).split("+")[0].split(/-(.*)/su);
|
|
85
|
+
const fields = core.split(".").map(field => Number(field) || 0);
|
|
86
|
+
return { fields, pre: pre ? pre.split(".") : [] };
|
|
87
|
+
};
|
|
88
|
+
const left = split(a);
|
|
89
|
+
const right = split(b);
|
|
90
|
+
for (let index = 0; index < 3; index += 1) {
|
|
91
|
+
const l = left.fields[index] ?? 0;
|
|
92
|
+
const r = right.fields[index] ?? 0;
|
|
93
|
+
if (l !== r) return l < r;
|
|
94
|
+
}
|
|
95
|
+
if (left.pre.length === 0) return false;
|
|
96
|
+
if (right.pre.length === 0) return true;
|
|
97
|
+
return comparePrerelease(left.pre, right.pre) < 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Parse a JSON file, or return null.
|
|
102
|
+
* @param {string} file Absolute path.
|
|
103
|
+
* @returns {any} Parsed JSON, or null when unreadable or malformed.
|
|
104
|
+
*/
|
|
105
|
+
function readJson(file) {
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The update-check cache path for a project — the CLI's own path.
|
|
115
|
+
* @param {string} projectDir Project root.
|
|
116
|
+
* @returns {string} Absolute cache path.
|
|
117
|
+
*/
|
|
118
|
+
export function npmLatestCachePath(projectDir) {
|
|
119
|
+
return path.join(
|
|
120
|
+
projectDir,
|
|
121
|
+
"node_modules",
|
|
122
|
+
".cache",
|
|
123
|
+
"@codyswann",
|
|
124
|
+
"lisa",
|
|
125
|
+
"update-check.json"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The Lisa version this project installed, or null when it has none to show.
|
|
131
|
+
*
|
|
132
|
+
* Null inside the Lisa monorepo itself: there `node_modules/@codyswann/lisa` is
|
|
133
|
+
* a fixture pinned majors behind the repository, and reporting it as the
|
|
134
|
+
* project's pin would tell every Lisa session its project is stale.
|
|
135
|
+
* @param {string} projectDir Project root.
|
|
136
|
+
* @returns {{version: string, source: string} | null} The installed copy.
|
|
137
|
+
*/
|
|
138
|
+
export function projectPin(projectDir) {
|
|
139
|
+
const own = readJson(path.join(projectDir, "package.json"));
|
|
140
|
+
if (own?.name === LISA_PACKAGE) return null;
|
|
141
|
+
const source = path.join(
|
|
142
|
+
projectDir,
|
|
143
|
+
"node_modules",
|
|
144
|
+
"@codyswann",
|
|
145
|
+
"lisa",
|
|
146
|
+
"package.json"
|
|
147
|
+
);
|
|
148
|
+
const version = readJson(source)?.version;
|
|
149
|
+
return typeof version === "string" && VERSION_SHAPE.test(version)
|
|
150
|
+
? { version, source }
|
|
151
|
+
: null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Read the cached npm latest.
|
|
156
|
+
* @param {string} cachePath Absolute cache path.
|
|
157
|
+
* @param {number} nowMs Current time in ms.
|
|
158
|
+
* @param {number} [ttlMs] Freshness window.
|
|
159
|
+
* @returns {{version: string, fetchedAt: string, fresh: boolean} | null} Cached value.
|
|
160
|
+
*/
|
|
161
|
+
export function cachedNpmLatest(cachePath, nowMs, ttlMs = NPM_LATEST_TTL_MS) {
|
|
162
|
+
const cache = readJson(cachePath);
|
|
163
|
+
const version = cache?.latest;
|
|
164
|
+
const fetchedAt = cache?.fetchedAt;
|
|
165
|
+
if (typeof version !== "string" || !VERSION_SHAPE.test(version)) return null;
|
|
166
|
+
if (typeof fetchedAt !== "string") return null;
|
|
167
|
+
const fetchedMs = Date.parse(fetchedAt);
|
|
168
|
+
if (!Number.isFinite(fetchedMs)) return null;
|
|
169
|
+
return { version, fetchedAt, fresh: nowMs - fetchedMs <= ttlMs };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Whether a refresh should be started for this session.
|
|
174
|
+
* @param {{version: string, fresh: boolean} | null} cached Cached value.
|
|
175
|
+
* @param {NodeJS.ProcessEnv} env Environment.
|
|
176
|
+
* @returns {boolean} True when the cache is missing or stale and checks are on.
|
|
177
|
+
*/
|
|
178
|
+
export function needsRefresh(cached, env) {
|
|
179
|
+
if (env.LISA_SKIP_UPDATE_CHECK === "1") return false;
|
|
180
|
+
return cached === null || !cached.fresh;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Start a detached refresh of the cache and return immediately.
|
|
185
|
+
* @param {string} script Absolute path of this module.
|
|
186
|
+
* @param {string} cachePath Absolute cache path.
|
|
187
|
+
* @param {typeof spawn} [spawnImpl] Injected for tests.
|
|
188
|
+
* @returns {boolean} Whether a child was started.
|
|
189
|
+
*/
|
|
190
|
+
export function startDetachedRefresh(script, cachePath, spawnImpl = spawn) {
|
|
191
|
+
try {
|
|
192
|
+
const child = spawnImpl(
|
|
193
|
+
process.execPath,
|
|
194
|
+
[script, "--refresh-npm-latest", cachePath],
|
|
195
|
+
{ detached: true, stdio: "ignore" }
|
|
196
|
+
);
|
|
197
|
+
child.on?.("error", () => {});
|
|
198
|
+
child.unref?.();
|
|
199
|
+
return true;
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Fetch npm latest and write it to the cache in the CLI's shape.
|
|
207
|
+
*
|
|
208
|
+
* probe-direction: neutral — a failed fetch writes nothing, so the next
|
|
209
|
+
* session reports "npm latest: unknown" (or the older cached value, labelled
|
|
210
|
+
* stale); no gate reads this value, it only shapes advisory context.
|
|
211
|
+
* @param {string} cachePath Absolute cache path.
|
|
212
|
+
* @param {{fetchImpl?: typeof fetch, now?: () => Date}} [deps] Injected for tests.
|
|
213
|
+
* @returns {Promise<string | null>} The version written, or null.
|
|
214
|
+
*/
|
|
215
|
+
export async function refreshNpmLatest(cachePath, deps = {}) {
|
|
216
|
+
const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
|
|
217
|
+
const now = deps.now ?? (() => new Date());
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetchImpl(NPM_LATEST_URL, {
|
|
220
|
+
signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
|
|
221
|
+
});
|
|
222
|
+
if (!response.ok) return null;
|
|
223
|
+
const body = await response.json();
|
|
224
|
+
const version = body?.version;
|
|
225
|
+
if (typeof version !== "string" || !VERSION_SHAPE.test(version)) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
229
|
+
writeFileSync(
|
|
230
|
+
cachePath,
|
|
231
|
+
`${JSON.stringify({ latest: version, fetchedAt: now().toISOString() }, null, 2)}\n`,
|
|
232
|
+
"utf8"
|
|
233
|
+
);
|
|
234
|
+
return version;
|
|
235
|
+
} catch {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Render the project and npm rows, plus the guidance when the project is behind.
|
|
242
|
+
* @param {{pin: {version: string, source: string} | null, latest: {version: string, fetchedAt: string, fresh: boolean} | null, refreshing: boolean, projectBehind: boolean}} state Resolved rows.
|
|
243
|
+
* @returns {{rows: string[], guidance: string[]}} Lines to splice into the block.
|
|
244
|
+
*/
|
|
245
|
+
export function renderNpmRows(state) {
|
|
246
|
+
const rows = [];
|
|
247
|
+
if (state.pin) {
|
|
248
|
+
rows.push(`project pin: lisa ${state.pin.version} at ${state.pin.source}`);
|
|
249
|
+
}
|
|
250
|
+
if (state.latest) {
|
|
251
|
+
rows.push(
|
|
252
|
+
`npm latest: lisa ${state.latest.version} (checked ${state.latest.fetchedAt}${state.latest.fresh ? "" : "; stale"}${state.refreshing ? ", refreshing for the next session" : ""})`
|
|
253
|
+
);
|
|
254
|
+
} else if (state.pin) {
|
|
255
|
+
rows.push(
|
|
256
|
+
`npm latest: unknown${state.refreshing ? " (checking in the background for the next session)" : ""}`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const guidance = state.projectBehind
|
|
260
|
+
? [
|
|
261
|
+
`PROJECT BEHIND — this project installs lisa ${state.pin?.version} while lisa ${state.latest?.version} is published.`,
|
|
262
|
+
"- Do NOT upgrade Lisa inside the current task: a version bump plus its template apply belongs in its own pull request, never mixed into feature work.",
|
|
263
|
+
"- Check for an open pull request on a `lisa/update-*` branch. If none is open, the project's Lisa Update workflow (`.github/workflows/lisa-update.yml`) opens one on its schedule; say so in your report, and if that workflow is missing, recommend running `lisa apply` once so the project receives it.",
|
|
264
|
+
]
|
|
265
|
+
: [];
|
|
266
|
+
return { rows, guidance };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Resolve everything the npm rows need, starting a refresh when warranted.
|
|
271
|
+
* @param {{projectDir: string, script?: string, env: NodeJS.ProcessEnv, nowMs: number, spawnImpl?: typeof spawn}} input Inputs.
|
|
272
|
+
* @returns {{pin: {version: string, source: string} | null, latest: {version: string, fetchedAt: string, fresh: boolean} | null, refreshing: boolean, projectBehind: boolean}} Resolved rows.
|
|
273
|
+
*/
|
|
274
|
+
export function resolveNpmState(input) {
|
|
275
|
+
try {
|
|
276
|
+
const pin = projectPin(input.projectDir);
|
|
277
|
+
if (pin === null) {
|
|
278
|
+
return { pin, latest: null, refreshing: false, projectBehind: false };
|
|
279
|
+
}
|
|
280
|
+
const cachePath = npmLatestCachePath(input.projectDir);
|
|
281
|
+
const latest = cachedNpmLatest(cachePath, input.nowMs);
|
|
282
|
+
const refreshing =
|
|
283
|
+
needsRefresh(latest, input.env) &&
|
|
284
|
+
startDetachedRefresh(
|
|
285
|
+
input.script ?? fileURLToPath(import.meta.url),
|
|
286
|
+
cachePath,
|
|
287
|
+
input.spawnImpl
|
|
288
|
+
);
|
|
289
|
+
const projectBehind = Boolean(
|
|
290
|
+
latest && precedes(pin.version, latest.version)
|
|
291
|
+
);
|
|
292
|
+
return { pin, latest, refreshing, projectBehind };
|
|
293
|
+
} catch {
|
|
294
|
+
return { pin: null, latest: null, refreshing: false, projectBehind: false };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Whether this module is the process entry point (the detached refresh child).
|
|
300
|
+
* Realpaths both sides for the reason `enforcement-vintage.mjs` gives.
|
|
301
|
+
* @param {string} moduleUrl This module's `import.meta.url`.
|
|
302
|
+
* @param {string | undefined} [argv1] Entry path.
|
|
303
|
+
* @returns {boolean} True when run directly.
|
|
304
|
+
*/
|
|
305
|
+
function invokedDirectly(moduleUrl, argv1 = process.argv[1]) {
|
|
306
|
+
if (!argv1) return false;
|
|
307
|
+
try {
|
|
308
|
+
return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (
|
|
315
|
+
invokedDirectly(import.meta.url) &&
|
|
316
|
+
process.argv[2] === "--refresh-npm-latest" &&
|
|
317
|
+
process.argv[3]
|
|
318
|
+
) {
|
|
319
|
+
refreshNpmLatest(process.argv[3]).catch(() => {});
|
|
320
|
+
}
|
|
@@ -52,6 +52,7 @@ import { readFileSync, existsSync } from "fs";
|
|
|
52
52
|
import path from "path";
|
|
53
53
|
import { fileURLToPath } from "url";
|
|
54
54
|
import { realpathSync } from "fs";
|
|
55
|
+
import { renderNpmRows, resolveNpmState } from "./enforcement-vintage-npm.mjs";
|
|
55
56
|
|
|
56
57
|
/** Marker the block is wrapped in, so an agent can cite it by name. */
|
|
57
58
|
const BLOCK_TAG = "lisa-enforcement-vintage";
|
|
@@ -336,6 +337,15 @@ export function renderBlock(state) {
|
|
|
336
337
|
`newest on this disk: lisa ${state.newest.version} at ${state.newest.source}`
|
|
337
338
|
);
|
|
338
339
|
}
|
|
340
|
+
const npm = renderNpmRows(
|
|
341
|
+
state.npm ?? {
|
|
342
|
+
pin: null,
|
|
343
|
+
latest: null,
|
|
344
|
+
refreshing: false,
|
|
345
|
+
projectBehind: false,
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
npm.rows.forEach(row => lines.push(row));
|
|
339
349
|
lines.push("");
|
|
340
350
|
if (state.behind || !state.running.version || state.treeBehind) {
|
|
341
351
|
lines.push(
|
|
@@ -363,6 +373,10 @@ export function renderBlock(state) {
|
|
|
363
373
|
"when you report on guard or contract behaviour; it is only current until the next release."
|
|
364
374
|
);
|
|
365
375
|
}
|
|
376
|
+
if (npm.guidance.length > 0) {
|
|
377
|
+
lines.push("");
|
|
378
|
+
npm.guidance.forEach(line => lines.push(line));
|
|
379
|
+
}
|
|
366
380
|
lines.push(`</${BLOCK_TAG}>`);
|
|
367
381
|
return lines.join("\n");
|
|
368
382
|
}
|
|
@@ -405,9 +419,14 @@ export function run(argv, stdin, env = process.env) {
|
|
|
405
419
|
return JSON.stringify({
|
|
406
420
|
hookSpecificOutput: {
|
|
407
421
|
hookEventName: eventName(stdin),
|
|
408
|
-
additionalContext: renderBlock(
|
|
409
|
-
resolveState({ hooksDir, projectDir, configDir })
|
|
410
|
-
|
|
422
|
+
additionalContext: renderBlock({
|
|
423
|
+
...resolveState({ hooksDir, projectDir, configDir }),
|
|
424
|
+
npm: resolveNpmState({
|
|
425
|
+
projectDir,
|
|
426
|
+
env,
|
|
427
|
+
nowMs: Date.now(),
|
|
428
|
+
}),
|
|
429
|
+
}),
|
|
411
430
|
},
|
|
412
431
|
});
|
|
413
432
|
}
|
|
@@ -367,6 +367,7 @@ Substrate column meanings (ordering per `credential-substrate-precedence`):
|
|
|
367
367
|
| `transition key:<K> to:<S>` | guarded fallback only: `acli jira workitem transition --key <K> --status "<S>" --yes` + post-read tenant assertion | `mcp__plugin_atlassian_atlassian__transitionJiraIssue` | resolve transition id then `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue/<K>/transitions` |
|
|
368
368
|
| `transitions key:<K>` — **false friend:** available transitions from current status, **NOT** past history; for history use `changelog` | (not exposed) | `mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>/transitions` |
|
|
369
369
|
| `changelog key:<K>` (read; ordered past status transitions) | (not exposed) | (not exposed) | `GET https://<SITE>/rest/api/3/issue/<K>?expand=changelog` |
|
|
370
|
+
| `comments key:<K>` (read; all comments, paginated) — **not** `comment`, which is the write | (not exposed) | `mcp__plugin_atlassian_atlassian__getJiraIssue` (comment field only; may be capped — page via curl when `total` exceeds what it returned) | `GET https://<SITE>/rest/api/3/issue/<K>/comment?startAt=<n>&maxResults=100&orderBy=created` |
|
|
370
371
|
| `comment key:<K> body:<B>` | guarded fallback only: `acli jira workitem comment add --key <K> --body "<B>"` + post-read tenant assertion | `mcp__plugin_atlassian_atlassian__addCommentToJiraIssue` | `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue/<K>/comment` |
|
|
371
372
|
| `link from:<K> to:<K2> type:<T>` | guarded fallback only: `acli jira workitem link create --in <K> --out <K2> --type "<T>" --yes` + direction and tenant assertion (see direction note) | `mcp__plugin_atlassian_atlassian__createJiraIssueLink` | `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issueLink` |
|
|
372
373
|
| `remote-links key:<K>` | (not exposed) | `mcp__plugin_atlassian_atlassian__getJiraIssueRemoteIssueLinks` | `GET https://<SITE>/rest/api/3/issue/<K>/remotelink` |
|
|
@@ -423,6 +424,17 @@ Operations not in this table are unsupported — add an adapter row before using
|
|
|
423
424
|
- **Pagination / truncation.** The issue-resource changelog (`?expand=changelog`) truncates busy issues (`changelog.maxResults`/`total`/`startAt`). When `total` exceeds what the issue resource returned, page the dedicated endpoint `GET https://<SITE>/rest/api/3/issue/<K>/changelog?startAt=<n>` until `startAt + maxResults >= total`, preserving order across pages. A silently truncated history is a correctness bug for detection.
|
|
424
425
|
- **Graceful degrade — never block the build.** A failed changelog fetch (network, auth, missing substrate) returns the substrate contract's `Error:` result. Callers MUST treat that as **unknown** history and proceed — a history read failure never blocks the build.
|
|
425
426
|
|
|
427
|
+
### `comments` — every comment on one issue
|
|
428
|
+
|
|
429
|
+
`comments key:<K>` returns every comment on a JIRA issue, oldest first. `read-ticket` embeds only the first page of comments in the issue resource (`fields.comment` carries its own `startAt`/`maxResults`/`total`), so a busy ticket's later comments — often the decisions and constraints — are missing from it.
|
|
430
|
+
|
|
431
|
+
- **Substrate.** JIRA REST `GET https://<SITE>/rest/api/3/issue/<K>/comment?startAt=<n>&maxResults=100&orderBy=created` (a read, so the `<SITE>` gateway is allowed after the token account check). acli exposes no paginated comment list; the MCP `getJiraIssue` comment field is used only when no token is available and is subject to the same first-page cap.
|
|
432
|
+
- **Shape.** For each entry in `comments[]` emit `{ id, author, created, body }` — `author.displayName`/`accountId`, `created` (ISO timestamp), and `body` converted from ADF to plain text with headings, lists, code, and links preserved.
|
|
433
|
+
- **Pagination.** Start at `startAt=0` and request the next page until `startAt + maxResults >= total`, preserving order across pages. Never stop at the first page.
|
|
434
|
+
- **Empty is valid.** An issue with no comments returns `total: 0`; that is a result, not an error.
|
|
435
|
+
- **Completeness fields.** Every result carries `comments_complete`, `comments_fetched`, and `comments_total`; `comments_complete: true` only when `comments_fetched == comments_total`. When only the MCP substrate is available and it cannot page past its first batch, return what it gave with `comments_complete: false` and the fetched-versus-total counts — never present a capped set as the whole.
|
|
436
|
+
- **Graceful degrade.** A failed page (network, auth, missing substrate) returns the substrate contract's `Error:` result for that page. Callers record the issue's comments as **incomplete** — naming how many of `total` were read — so a partial set is never silently truncated into looking complete.
|
|
437
|
+
|
|
426
438
|
### Step 4 — Return result
|
|
427
439
|
|
|
428
440
|
Emit either:
|
|
@@ -18,10 +18,10 @@ Create conventional commits for current changes. Optional hint: $ARGUMENTS
|
|
|
18
18
|
### Apply these requirements
|
|
19
19
|
|
|
20
20
|
1. **Branch Check**: If on `dev`, `staging`, or `main`, create a feature branch named after the changes
|
|
21
|
-
2. **Commit Strategy**: Group related changes into logical conventional commits (feat, fix, chore, docs, etc.)
|
|
22
|
-
3. **Commit ALL Files**: Every file must be assigned to a commit group - no file gets left out or unstaged
|
|
21
|
+
2. **Commit Strategy**: Group related changes into logical conventional commits (feat, fix, chore, docs, etc.). Every rule below that says "all" or "everything" excludes the local-only `.lisa/work-item-context.md` described in rule 3.
|
|
22
|
+
3. **Commit ALL Files**: Every file must be assigned to a commit group - no file gets left out or unstaged. **Local-only exception:** never stage `.lisa/work-item-context.md`, or any other file the work-item context contract marks local-only, even when it is untracked and not ignored — it can quote credentials from tracker comments. Leave it untracked, and say in your report that it was left out and why.
|
|
23
23
|
4. **Commit Creation**: Stage and commit each group with clear messages
|
|
24
|
-
5. **Verification**: Run `git status` to confirm working directory is clean - must show "nothing to commit"
|
|
24
|
+
5. **Verification**: Run `git status` to confirm working directory is clean - must show "nothing to commit" (apart from a local-only file left untracked under rule 3)
|
|
25
25
|
|
|
26
26
|
### Use conventional commit format
|
|
27
27
|
|
|
@@ -43,10 +43,10 @@ the original authored line may remain in the message.
|
|
|
43
43
|
- use `--no-verify` flag
|
|
44
44
|
- attempt to bypass tests or quality checks
|
|
45
45
|
- skip tests or quality checks
|
|
46
|
-
- stash changes - ALL changes must be committed
|
|
47
|
-
- skip or exclude any files from the commit - even if they're unrelated
|
|
48
|
-
- leave uncommitted changes in the working directory
|
|
49
|
-
- ask the user which files to commit - commit everything
|
|
46
|
+
- stash changes - ALL changes must be committed, except the local-only `.lisa/work-item-context.md`
|
|
47
|
+
- skip or exclude any files from the commit - even if they're unrelated (the local-only `.lisa/work-item-context.md` is the one exclusion, and it is mandatory)
|
|
48
|
+
- leave uncommitted changes in the working directory, other than the local-only `.lisa/work-item-context.md`
|
|
49
|
+
- ask the user which files to commit - commit everything except the local-only `.lisa/work-item-context.md`
|
|
50
50
|
|
|
51
51
|
## Execute
|
|
52
52
|
|
|
@@ -27,7 +27,7 @@ Recognized optional hints:
|
|
|
27
27
|
### Apply these requirements
|
|
28
28
|
|
|
29
29
|
1. **Branch Check**: Verify not on `dev`, `staging`, or `main` (cannot create PR from protected branches)
|
|
30
|
-
2. **Commit Check**: Ensure all changes are committed before pushing
|
|
30
|
+
2. **Commit Check**: Ensure all changes are committed before pushing — except the local-only `.lisa/work-item-context.md`, which is never staged even when untracked and not ignored; an untracked copy of it does not fail this check.
|
|
31
31
|
2a. **Local review before push**: Unless `local_review=done` has current evidence, run `lisa-review-local` over the branch diff, address findings through `convergent-review`, and commit any fixes before step 3. This applies regardless of third-party reviewer availability. Reuse an applicable completed review instead of repeating it.
|
|
32
32
|
|
|
33
33
|
Report the result as *self-reviewed*; it does not satisfy the ruleset-required third-party review check. If the runtime cannot delegate review, record **local review unavailable**, continue under the existing merge policy, and do not pass `local_review=done`. An unavailable review is not a passing review.
|
|
@@ -517,7 +517,7 @@ After the claim succeeds, run the per-issue lifecycle defined by the `github-age
|
|
|
517
517
|
- `lisa-github-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
|
|
518
518
|
- `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
|
|
519
519
|
- Intent determination from the `type:` label
|
|
520
|
-
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this item `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
520
|
+
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. Hand that bundle over by file, not by text: before invoking `lisa-implement`, run the ignore guard below, then write the bundle verbatim to persist it at the git-ignored `.lisa/work-item-context.md` of this worktree — always overwriting the file with this cycle's bundle, never reusing one an earlier item left behind — and pass only `caller_bundle_path=<absolute path>` in the invocation. `lisa-implement`'s input-resolver keeps that file as the base instead of re-summarizing it, so every comment reaches the agents doing the work. Bundle text, and any credential value in it, never appears in a spawn prompt, task description, or Skill argument — only its path. **Ignore guard before any write.** A host can receive these skills before its shipped `.gitignore` entry, so prove the path is ignored first, from the worktree root: `git check-ignore -q .lisa/work-item-context.md || printf '%s\n' '.lisa/work-item-context.md' >> "$(git rev-parse --git-common-dir)/info/exclude"`, then re-run `git check-ignore -q .lisa/work-item-context.md`. If it is still not ignored, stop and report — do not write the bundle. If an `.easignore` exists and `grep -qxF '.lisa/work-item-context.md' .easignore` fails, stop and report instead of writing: EAS uploads read `.easignore`, which is derived from `.gitignore`, not from `info/exclude`. **When 3b classified this item `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
521
521
|
3. **Milestone sync and evidence** (`lisa-github-sync`, `lisa-github-evidence`) happen at the milestones the `github-agent` workflow defines, within the dispatched flow.
|
|
522
522
|
|
|
523
523
|
If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
|
|
@@ -63,13 +63,13 @@ Any other `##` section: capture under `extra_sections` so callers can see PRDs t
|
|
|
63
63
|
|
|
64
64
|
### Comments
|
|
65
65
|
|
|
66
|
-
Fetch ALL comments.
|
|
66
|
+
Fetch ALL comments, always through the paginated endpoint, flattened into one array: `gh api repos/<org>/<repo>/issues/<number>/comments --paginate --slurp | jq 'add // []'` (author, body, created_at for each). `--paginate` alone emits one JSON array per page, so reading or counting its raw output undercounts; `--slurp` plus `jq 'add'` flattens the pages first. The `comments` field of `gh issue view --json` is not the source of record — it can be capped. Do not truncate. The fetched count is the number of comments after flattening (`... | jq 'add // [] | length'`); compare it with the issue's `comments` total (`gh api repos/<org>/<repo>/issues/<number> --jq .comments`); if they differ or a page fails, set `comments_complete: false` and say so at the top of the Comments section. Flag comments that contain:
|
|
67
67
|
- Credentials, reproduction steps
|
|
68
68
|
- Status updates from stakeholders
|
|
69
69
|
- Decisions
|
|
70
70
|
- Triage headers like `[<repo>]`
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
Pagination is unconditional — never skip `--paginate` because an issue looks short.
|
|
73
73
|
|
|
74
74
|
## Phase 3 — Fetch Sub-issue Graph (Parent + Children)
|
|
75
75
|
|
|
@@ -244,7 +244,8 @@ Produce a single structured output that the caller can pass verbatim to downstre
|
|
|
244
244
|
#### Source Artifacts / Source Precedence / Links / Relationship Search / Repository / Sign-in Required / Target Backend Environment / Open Questions / Current Product
|
|
245
245
|
<each verbatim, omit those not present>
|
|
246
246
|
|
|
247
|
-
### Comments (<
|
|
247
|
+
### Comments (<fetched> of <total>; comments_complete: <true|false>)
|
|
248
|
+
<when incomplete: "INCOMPLETE — <fetched> of <total> comments read via <substrate>">
|
|
248
249
|
<chronological comments with author + ISO timestamp + body. Flagged items called out.>
|
|
249
250
|
|
|
250
251
|
## Sub-issue graph
|
|
@@ -57,6 +57,8 @@ The team lead does NOT read the input directly. The first task on the team's pla
|
|
|
57
57
|
|
|
58
58
|
The input-resolver invokes `lisa-track $ARGUMENTS` and owns its complete resolve -> claim -> bind transaction:
|
|
59
59
|
|
|
60
|
+
**Forward a caller-supplied bundle by path only.** A build-intake or repair-intake dispatch has already written the bundle it read, verbatim, to the git-ignored `.lisa/work-item-context.md` and invokes this skill with `caller_bundle_path=<absolute path>`. The lead forwards only `caller_bundle_path: <absolute path>` in the input-resolver's spawn prompt, next to `lisa-track $ARGUMENTS`. Bundle text, and any credential value in it, never appears in a spawn prompt, task description, or Skill argument — only its path.
|
|
61
|
+
|
|
60
62
|
- **Explicit ticket:** call `lisa-tracker-read` against the configured tracker and require a live open/unresolved current-project leaf. **Mismatch guard:** if the ticket format/project does not match the configured tracker (for example, a GitHub URL when `tracker` is `jira`), stop — never auto-translate vendors or trust pasted/stale ticket text. The read captures comments, graph, and metadata, not just the description.
|
|
61
63
|
- **Specification file:** read the entire file without offset or limit, preserve it as the resolved input, and follow the plain-text resolution path below.
|
|
62
64
|
- **Plain text or file contents:** search the configured project conservatively for open leaves describing the same outcome. Live-validate every candidate through `lisa-tracker-read`; reuse only exactly one high-confidence match. When there is no unique match (zero or ambiguous candidates), synthesize one complete single-repository leaf and invoke `lisa-tracker-write` exactly once with `build_ready: true`, then live-read its canonical returned ref. Never create a thin placeholder, hierarchy, or container.
|
|
@@ -68,7 +70,9 @@ The input-resolver invokes `lisa-track $ARGUMENTS` and owns its complete resolve
|
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
Require a successful readback of that worktree-local binding. On detached HEAD, `branch: null` is the expected pending binding; after branch creation the mandatory `attach-branch` step below must replace it before any commit. Tracker or binding failure stops the flow; never continue untracked.
|
|
71
|
-
-
|
|
73
|
+
- **Persist the bundle verbatim to `.lisa/work-item-context.md`.** **Ignore guard before any write.** A host can receive these skills before its shipped `.gitignore` entry, so prove the path is ignored first, from the worktree root: `git check-ignore -q .lisa/work-item-context.md || printf '%s\n' '.lisa/work-item-context.md' >> "$(git rev-parse --git-common-dir)/info/exclude"`, then re-run `git check-ignore -q .lisa/work-item-context.md`. If it is still not ignored, stop and report — do not write the bundle. If an `.easignore` exists and `grep -qxF '.lisa/work-item-context.md' .easignore` fails, stop and report instead of writing: EAS uploads read `.easignore`, which is derived from `.gitignore`, not from `info/exclude`. There is one writer order. `lisa-track` has already written its live read, unedited, to `<worktree root>/.lisa/work-item-context.md`. With no `caller_bundle_path`, keep that file as written. With a `caller_bundle_path`, `lisa-track` leaves an existing file untouched, and this step persists the caller-supplied bundle instead of discarding it: refuse a `caller_bundle_path` that does not end in `/.lisa/work-item-context.md` — do not read it, report the refusal to the lead, and keep the live read as the file; otherwise read the file at that path and keep it as the base (copy it verbatim to the worktree's `.lisa/work-item-context.md` first if the path differs), then append under `## Added by live read` only what the live read carries that the caller bundle lacks (newer comments, changed status or graph). Either way the live validation and claim still run exactly as above. Never summarize, trim, or paraphrase the bundle; downstream agents act on this file, so anything left out of it is lost to them. The file is worktree-local and git-ignored; never stage it.
|
|
74
|
+
- **Append the per-comment inventory to the file.** Finish the file with a trailing `## Comment inventory` section: one line per comment with author, date, one-line gist, and flags from `decision|constraint|credential|repro` for each decision, constraint, credential/access note, or reproduction step it carries. The inventory indexes the bundle above it; it never replaces it. **A `credential` gist names only the kind and the location** — for example `staging API token — see comment 4 in the context file` — never the secret value or identifier. The value stays only in the git-ignored context file: never copy it into task descriptions, teammate prompts, plan or roster files, tracker comments, or PR text.
|
|
75
|
+
- Return `work_item_context: <absolute path of the file>`, the same per-comment inventory, `tracker_provider`, canonical `work_item_ref`, resolution outcome, claim outcome, and verified binding to the team lead, who then proceeds to roster selection.
|
|
72
76
|
|
|
73
77
|
The input resolver may perform these tracker and local-binding operations before the Roster Decision because they are the mandatory gate that establishes what work the team is allowed to do. No project source, documentation, plan artifact, branch, or task may be created or changed before this transaction succeeds. Read-only discussion/orientation outside an Implement flow remains exempt per the `tracked-work` rule.
|
|
74
78
|
|
|
@@ -209,7 +213,9 @@ The same gate applies **continuously**: if a tool requirement surfaces mid-flow
|
|
|
209
213
|
|
|
210
214
|
Using the general-purpose agent in Team Lead session, create tasks needed to complete the request.
|
|
211
215
|
|
|
212
|
-
Every task MUST include this JSON metadata block. Do NOT omit `skills` (use `[]` if none), `learnings` (use `[]` if none), `required_access` (use `[]` if the task needs no external tool) or `verification`.
|
|
216
|
+
Every task MUST include this JSON metadata block. Do NOT omit `skills` (use `[]` if none), `learnings` (use `[]` if none), `required_access` (use `[]` if the task needs no external tool), `work_item_context`, or `verification`.
|
|
217
|
+
|
|
218
|
+
Every task description and every teammate prompt names the absolute `work_item_context` path the resolver returned (the worktree's `.lisa/work-item-context.md`), pastes the resolver's per-comment inventory, and tells the teammate to read the file in full before acting; each comment the inventory flagged is an obligation the plan and the verification must each address explicitly.
|
|
213
219
|
|
|
214
220
|
```json
|
|
215
221
|
{
|
|
@@ -217,6 +223,7 @@ Every task MUST include this JSON metadata block. Do NOT omit `skills` (use `[]`
|
|
|
217
223
|
"type": "spike|bug|task|epic|story",
|
|
218
224
|
"acceptance_criteria": ["..."],
|
|
219
225
|
"relevant_documentation": "",
|
|
226
|
+
"work_item_context": "<absolute path returned by the resolver, ending in .lisa/work-item-context.md>",
|
|
220
227
|
"testing_requirements": ["..."],
|
|
221
228
|
"skills": ["..."],
|
|
222
229
|
"learnings": [{ "kind": "mistake", "note": "one line", "evidence": "optional ref" }],
|
|
@@ -347,7 +354,7 @@ Before shutting down the team, execute the Verify flow:
|
|
|
347
354
|
`claim-evidence-mapping` contract by slug and continue; never block on the absent surface.
|
|
348
355
|
3. Write the highest-practical-observation regression test encoding the verification. For user-visible bugs or user-visible Build changes with an available browser/device/e2e harness, this means a deterministic spec on the reported surface — and for frontend work, once the validation journey is verified, the scenario and its aligned automation for **every platform the scenario requires**, per `codify-verification` and the `bdd-e2e-coverage` rule. Prove the new spec actually executed and passed in PR CI by recording a named spec log/reporter line or equivalent execution record; green CI without that named evidence does not satisfy this step.
|
|
349
356
|
4. Record Implement usage on the originating work artifact via `lisa-usage-accounting` so the work item (or other implementation-owned artifact) gains a direct `lisa-implement` usage entry in the canonical `## Lisa Usage` section. If the parent / child graph is already known, prefer `record_and_rollup` so ancestor totals refresh in the same write; otherwise still write the direct entry, and if runtime usage is unavailable, use `source: unavailable` with nullable token/cost fields instead of skipping the row.
|
|
350
|
-
5. Commit ALL outstanding changes in logical batches on the branch (minus sensitive data/information) — not just changes made by the agent team. This includes pre-existing uncommitted changes that were on the branch before the plan started. Do NOT filter commits to only "task-related" files. If it shows up in git status, it gets committed (unless it contains secrets).
|
|
357
|
+
5. Commit ALL outstanding changes in logical batches on the branch (minus sensitive data/information) — not just changes made by the agent team. This includes pre-existing uncommitted changes that were on the branch before the plan started. Do NOT filter commits to only "task-related" files. If it shows up in git status, it gets committed (unless it contains secrets) — except the local-only `.lisa/work-item-context.md`, which is never staged even when untracked and not ignored; leave it untracked and say so.
|
|
351
358
|
6. Delegate local review and push to `lisa-git-submit-pr` in step 7. Pass `local_review=done` only when a completed review result covers the current branch diff; otherwise omit the hint so submit-pr runs the review. Do not push separately before that review.
|
|
352
359
|
|
|
353
360
|
7. Open a pull request with auto-merge on via `lisa-git-submit-pr`, targeting the **base branch resolved from the ticket's environment** (`target_branch=<base>`, per the branch step above), and including the mandatory work-item ref so the PR can be linked natively to the source issue.
|
|
@@ -360,6 +367,6 @@ Before shutting down the team, execute the Verify flow:
|
|
|
360
367
|
13. `ops-specialist`: post-deploy health check via `lisa-monitor <env> --report-only`, monitor for errors in first minutes. `--report-only` is REQUIRED: it keeps the post-deploy check a pure health/audit report so monitor's standalone ticket-filing never fires inside this flow.
|
|
361
368
|
14. If remote verification fails, create a task for the agent team to find out why it failed, fix it and return to step 5. **Bound this loop**: after a small number of full fix→deploy→reverify cycles without reaching a passing remote verdict (treat ~3 as the ceiling unless the work item states otherwise), stop retrying — file a build-ready fix ticket, write the verdict with `status: "blocked"` and the diagnosis, and move the work item to blocked rather than looping indefinitely. The completion gate releases on a `blocked` verdict, so the flow ends with a recorded outcome instead of a silent spin or a self-declared success.
|
|
362
369
|
15. **Post evidence to the originating work item** via `lisa-tracker-evidence` (vendor-neutral; dispatches to `lisa-jira-evidence`, `lisa-github-evidence`, or `lisa-linear-evidence` per `.lisa.config.json` `tracker`), including the list of codified tests added on this branch. **If the work is UI-visible** (any verification step ran in a browser, or the change touches a user-facing surface), author `evidence/comment.md` per the **UI Evidence Checklist** in `lisa-tracker-evidence` — numbered live-session steps, one screenshot per step captured through the interactive browser controller and uploaded to the GitHub `pr-assets` release as plain URLs, and an explicit invitation to be corrected. This step is what makes the proof visible to a non-technical operator standing at the gate; a terminal work item with no posted evidence is an incomplete flow, not a finished one.
|
|
363
|
-
16. After true terminal completion — required merge/deploy/verification passed, usage/evidence and two-way PR linkage are recorded, and the work item is terminal — run `node scripts/lisa-work-item.mjs clear` and verify the worktree has no current binding. Do not clear on an interruption or blocked outcome; preserving the binding is what keeps resumed work attributable.
|
|
370
|
+
16. After true terminal completion — required merge/deploy/verification passed, usage/evidence and two-way PR linkage are recorded, and the work item is terminal — run `node scripts/lisa-work-item.mjs clear`, delete the work-item context file (`rm -f .lisa/work-item-context.md` from the worktree root) so comment-borne secrets do not outlive the work, and verify the worktree has no current binding and no context file. Do not clear on an interruption or blocked outcome; preserving the binding is what keeps resumed work attributable.
|
|
364
371
|
|
|
365
372
|
**Ticket status transitions throughout this flow are config-bound** per the **Tracker status vocabulary** section of the `config-resolution` rule: whoever performs the tracker write — the lead included, in runtimes where the tracker MCP is lead-only — may only target statuses named in the configured workflow map (`claimed`, `blocked`, env-keyed `done`, and `review`/`qa` when configured). Never adopt a status discovered from the tracker's live workflow; a lifecycle stage with no configured status gets a comment, not a transition.
|
|
@@ -388,7 +388,7 @@ After the claim succeeds, run the per-ticket lifecycle defined by the `jira-agen
|
|
|
388
388
|
- `lisa-jira-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
|
|
389
389
|
- `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
|
|
390
390
|
- Intent determination from the issue type
|
|
391
|
-
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this ticket `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
391
|
+
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. Hand that bundle over by file, not by text: before invoking `lisa-implement`, run the ignore guard below, then write the bundle verbatim to persist it at the git-ignored `.lisa/work-item-context.md` of this worktree — always overwriting the file with this cycle's bundle, never reusing one an earlier item left behind — and pass only `caller_bundle_path=<absolute path>` in the invocation. `lisa-implement`'s input-resolver keeps that file as the base instead of re-summarizing it, so every comment reaches the agents doing the work. Bundle text, and any credential value in it, never appears in a spawn prompt, task description, or Skill argument — only its path. **Ignore guard before any write.** A host can receive these skills before its shipped `.gitignore` entry, so prove the path is ignored first, from the worktree root: `git check-ignore -q .lisa/work-item-context.md || printf '%s\n' '.lisa/work-item-context.md' >> "$(git rev-parse --git-common-dir)/info/exclude"`, then re-run `git check-ignore -q .lisa/work-item-context.md`. If it is still not ignored, stop and report — do not write the bundle. If an `.easignore` exists and `grep -qxF '.lisa/work-item-context.md' .easignore` fails, stop and report instead of writing: EAS uploads read `.easignore`, which is derived from `.gitignore`, not from `info/exclude`. **When 3b classified this ticket `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
392
392
|
3. **Milestone sync and evidence** (`lisa-jira-sync`, `lisa-jira-evidence`) happen at the milestones the `jira-agent` workflow defines, within the dispatched flow.
|
|
393
393
|
|
|
394
394
|
If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
|