@pome-sh/cli 0.15.0 → 0.16.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/dist/build-info.json +3 -3
- package/dist/src/cli/checks.d.ts +3 -2
- package/dist/src/cli/checks.js +22 -9
- package/node_modules/@pome-sh/twin-linear/CHANGELOG.md +41 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.d.ts +10 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js +117 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.d.ts +25 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js +211 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-kind.d.ts +3 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-kind.js +9 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-kind.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-params.d.ts +8 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-params.js +94 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-params.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.d.ts +98 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js +131 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-tape.d.ts +2 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-tape.js +100 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-tape.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-worlds.d.ts +44 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-worlds.js +93 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-worlds.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/checks.d.ts +29 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/checks.js +52 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/checks.js.map +1 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/index.d.ts +2 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/index.js +7 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/index.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/package.json +1 -1
- package/package.json +2 -2
- package/tasks/24-linear-issue-triage.md +2 -2
- package/tasks/25-linear-comment-label-triage.md +3 -3
- package/tasks/26-github-linear-handoff.md +3 -4
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "pome-sh",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"git_sha": "
|
|
5
|
-
"build_time": "2026-07-
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"git_sha": "1d69d644c3acee14dc92d621968b1322c40a0bf0",
|
|
5
|
+
"build_time": "2026-07-31T00:23:39.731Z"
|
|
6
6
|
}
|
package/dist/src/cli/checks.d.ts
CHANGED
|
@@ -11,8 +11,9 @@ export declare function twinsWithChecks(): string[];
|
|
|
11
11
|
* assert the behaviour, which is the same defect F-1075 hit with a hard-coded
|
|
12
12
|
* picker index.
|
|
13
13
|
*
|
|
14
|
-
* A3 empties this list
|
|
15
|
-
*
|
|
14
|
+
* A3 empties this list, and F-1129 answered the question that raised: the PATH
|
|
15
|
+
* stays, the LITERAL goes. It is now `MOUNTED_TWINS` minus the registry, so an
|
|
16
|
+
* empty result is a fact about the world rather than a list nobody updated.
|
|
16
17
|
*/
|
|
17
18
|
export declare function twinsWithoutChecks(): string[];
|
|
18
19
|
export declare function checksFor(twin: string): readonly DeclaredCheck[];
|
package/dist/src/cli/checks.js
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
import { readFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { checksDigest, templateSlots } from "@pome-sh/sdk/checks";
|
|
14
|
+
import { MOUNTED_TWINS } from "@pome-sh/shared-types";
|
|
14
15
|
import { GITHUB_CHECKS } from "@pome-sh/twin-github/checks";
|
|
15
16
|
import { GMAIL_CHECKS } from "@pome-sh/twin-gmail/checks";
|
|
17
|
+
import { LINEAR_CHECKS } from "@pome-sh/twin-linear/checks";
|
|
16
18
|
import { SLACK_CHECKS } from "@pome-sh/twin-slack/checks";
|
|
17
19
|
import { STRIPE_CHECKS } from "@pome-sh/twin-stripe/checks";
|
|
18
20
|
import { resolvePackageRoot } from "./resolve-package-root.js";
|
|
@@ -21,15 +23,25 @@ const REGISTRIES = {
|
|
|
21
23
|
slack: SLACK_CHECKS,
|
|
22
24
|
stripe: STRIPE_CHECKS,
|
|
23
25
|
gmail: GMAIL_CHECKS,
|
|
26
|
+
linear: LINEAR_CHECKS,
|
|
24
27
|
};
|
|
25
|
-
// Twins that EXIST but declare nothing yet
|
|
26
|
-
// linear` answers "not migrated yet" instead of "no such twin" — a typo and an
|
|
27
|
-
// empty vocabulary are different facts. F-1126 removed slack; F-1127 removed
|
|
28
|
-
// stripe; F-1128 removed gmail, which is the first twin whose migration needed
|
|
29
|
-
// a seed loader in pome-cloud before its vocabulary could grade anything.
|
|
28
|
+
// Twins that EXIST but declare nothing yet — DERIVED, not listed.
|
|
30
29
|
//
|
|
31
|
-
//
|
|
32
|
-
|
|
30
|
+
// The distinction is still real: `pome checks <twin>` must answer "not migrated
|
|
31
|
+
// yet" for a twin that exists, and "no such twin" for a typo. What is no longer
|
|
32
|
+
// real is the hand-maintained literal. F-1126 removed slack from it, F-1127
|
|
33
|
+
// stripe, F-1128 gmail, and F-1129 would have emptied it — four tickets each
|
|
34
|
+
// editing one line of a list that `MOUNTED_TWINS` already knows.
|
|
35
|
+
//
|
|
36
|
+
// So it is a set difference instead. Today it is EMPTY, which is A3's whole
|
|
37
|
+
// acceptance criterion. The day a sixth twin mounts, it repopulates itself and
|
|
38
|
+
// the "not migrated yet" path comes back live without anyone remembering to add
|
|
39
|
+
// a literal — which is the failure this project is named for, one level down.
|
|
40
|
+
// Annotated `string[]` on purpose: `MOUNTED_TWINS` is a literal-union tuple, so
|
|
41
|
+
// the filtered result keeps that union and `isKnownTwin` cannot ask it about an
|
|
42
|
+
// arbitrary string. Widening here is what keeps the caller's question — "is this
|
|
43
|
+
// user-typed word a twin?" — expressible.
|
|
44
|
+
const TWINS_WITHOUT_CHECKS = MOUNTED_TWINS.filter((twin) => !(twin in REGISTRIES));
|
|
33
45
|
export function twinsWithChecks() {
|
|
34
46
|
return Object.keys(REGISTRIES).sort();
|
|
35
47
|
}
|
|
@@ -43,8 +55,9 @@ export function twinsWithChecks() {
|
|
|
43
55
|
* assert the behaviour, which is the same defect F-1075 hit with a hard-coded
|
|
44
56
|
* picker index.
|
|
45
57
|
*
|
|
46
|
-
* A3 empties this list
|
|
47
|
-
*
|
|
58
|
+
* A3 empties this list, and F-1129 answered the question that raised: the PATH
|
|
59
|
+
* stays, the LITERAL goes. It is now `MOUNTED_TWINS` minus the registry, so an
|
|
60
|
+
* empty result is a fact about the world rather than a list nobody updated.
|
|
48
61
|
*/
|
|
49
62
|
export function twinsWithoutChecks() {
|
|
50
63
|
return [...TWINS_WITHOUT_CHECKS];
|
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @pome-sh/twin-linear — CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-07-30
|
|
4
|
+
|
|
5
|
+
Linear declares its assertable check vocabulary (F-1129, milestone A3) — the
|
|
6
|
+
last of the five twins, and the one whose migration could not be a rename.
|
|
7
|
+
|
|
8
|
+
- New `./checks` subpath: `LINEAR_CHECKS`, eight declarations, plus the
|
|
9
|
+
`LinearCheckState` model they read (`check-state.ts`). pome-cloud deletes its
|
|
10
|
+
hand-maintained mirror of that shape in the same milestone — the twin's model
|
|
11
|
+
is now the only one. `LINEAR_CHECKS` is also re-exported from the package
|
|
12
|
+
root, as twin-slack does.
|
|
13
|
+
- **Every check names its TEAM**, where twin-slack's vocabulary names no scope
|
|
14
|
+
at all. `seed.ts` validates issue-title uniqueness via `issueTitlesByTeam` —
|
|
15
|
+
per team, not per workspace — so a title-keyed selector over a two-team world
|
|
16
|
+
is exactly the ambiguity twin-github's repo rule exists to close. The contract
|
|
17
|
+
test enforces it, and only a check that reads no state may be ledgered out.
|
|
18
|
+
- **A selector miss FAILS; a truncated export SKIPS.** twin-github fails on a
|
|
19
|
+
miss and twin-slack skips; Linear needs both from one resolver, so
|
|
20
|
+
`Resolved<T>` carries the disposition. The split is evidenced rather than
|
|
21
|
+
chosen: `state.ts` exports `exportBounds.truncatedCollections`, so the twin
|
|
22
|
+
itself reports when rows were dropped past `STATE_EXPORT_CAP`. That is the
|
|
23
|
+
only place in this vocabulary where "not found" and "absent" are different
|
|
24
|
+
facts, and neither `GitHubCheckState` nor `SlackCheckState` has an analogue.
|
|
25
|
+
- Five checks read an issue row — `linear.issue-exists`, `linear.issue-state`,
|
|
26
|
+
`linear.issue-has-label`, `linear.issue-estimate`, `linear.issue-assignee` —
|
|
27
|
+
two read comments, one reads the tape. `linear.issue-threaded-reply` is the
|
|
28
|
+
only `seed+final` member: it asserts a reply whose parent existed IN THE SEED,
|
|
29
|
+
because a final-only reading is satisfied by an agent that posts a comment and
|
|
30
|
+
then replies to itself.
|
|
31
|
+
- `linear.no-unsupported-endpoint` drops the twin word — it is now the same
|
|
32
|
+
sentence twin-github and twin-gmail declare, resolved per-twin by the engine.
|
|
33
|
+
- **`linear.issue-lifecycle` is NOT migrated**, and that is a narrowing rather
|
|
34
|
+
than an oversight. A declared `... is {lifecycle}` near-misses every
|
|
35
|
+
`is in state "..."` sentence and would report a corrupted state criterion under
|
|
36
|
+
a check the author never picked. It had zero corpus users; `is in state "Done"`
|
|
37
|
+
/ `"Canceled"` re-expresses what mattered.
|
|
38
|
+
- `cli/tasks/24`, `25` and `26` are rewritten in the same commit. Six of the nine
|
|
39
|
+
shipped criteria named their subject with "that issue", with no subject at all,
|
|
40
|
+
or by pointing at an id in the seed — and a rendered sentence cannot carry a
|
|
41
|
+
pronoun, because a check only ever sees its own arguments. Task 26 LOSES a
|
|
42
|
+
criterion rather than gaining a subject: `linear.issue-state` fails on a
|
|
43
|
+
missing issue and already subsumes `An issue titled "..." exists`.
|
|
3
44
|
|
|
4
45
|
## 0.1.1 — 2026-07-30
|
|
5
46
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// What Linear's declared checks can assert about COMMENTS (F-1129).
|
|
4
|
+
//
|
|
5
|
+
// The threaded-reply check is the vocabulary's only `seed+final` member, and it
|
|
6
|
+
// is a delta for a reason task 25 states in its own prompt: "Reply in-thread to
|
|
7
|
+
// the EXISTING comment." A final-only reading — "some comment on this issue has
|
|
8
|
+
// a parentId" — is satisfied by an agent that posts a root comment and then
|
|
9
|
+
// replies to itself, which is not the behaviour the task examines. Comparing
|
|
10
|
+
// against the seed is what makes "the existing comment" mean something the
|
|
11
|
+
// grader can see.
|
|
12
|
+
//
|
|
13
|
+
// It also replaces a criterion that named an internal id
|
|
14
|
+
// ("A reply comment exists with parentId equal to the seeded root comment").
|
|
15
|
+
// Under position 2 an author picks a check and fills its parameters, so a
|
|
16
|
+
// sentence cannot reach into the seed for an identifier it never typed.
|
|
17
|
+
import { defineCheck, VACUITY_SENTINEL } from "@pome-sh/sdk/checks";
|
|
18
|
+
import { commentNeedle, issueTitle, teamKey } from "./check-params.js";
|
|
19
|
+
import { resolveComments, resolveIssue, unresolved } from "./check-state.js";
|
|
20
|
+
import { deltaWorld, finalWorld, issueRow, linearState } from "./check-worlds.js";
|
|
21
|
+
export const issueCommentContains = defineCheck({
|
|
22
|
+
id: "linear.issue-comment-contains",
|
|
23
|
+
description: "Resolves the issue and scans the body of every comment on it for this string as a " +
|
|
24
|
+
"SUBSTRING, case-sensitively. Because the string is hunted inside free prose rather than " +
|
|
25
|
+
"compared to a field, a redactor that destroys it makes this check unable to fire — the " +
|
|
26
|
+
"engine skips it as `subject_redacted` rather than passing it vacuously. Choose the " +
|
|
27
|
+
"needle every honest phrasing would share, not the whole sentence you imagine the agent " +
|
|
28
|
+
'writing: a task wanting a GitHub cross-reference is served by "#1", where "GitHub issue ' +
|
|
29
|
+
'#1" fails an agent that wrote "linked from acme/api#1".',
|
|
30
|
+
template: 'A comment on issue "{title}" in `{team}` contains "{needle}"',
|
|
31
|
+
params: { title: issueTitle, team: teamKey, needle: commentNeedle },
|
|
32
|
+
substrate: "final",
|
|
33
|
+
polarity: () => "positive",
|
|
34
|
+
// The needle is SCANNED inside prose, so it is the subject — twin-slack's
|
|
35
|
+
// `messageNeedle` precedent, and the reason a redacted needle becomes an
|
|
36
|
+
// honest skip rather than a silent failure.
|
|
37
|
+
subject: ({ needle }) => needle,
|
|
38
|
+
vacuityMutant: (args) => ({ ...args, needle: VACUITY_SENTINEL }),
|
|
39
|
+
discriminatingWorlds: ({ title, needle }) => ({
|
|
40
|
+
passing: finalWorld(linearState([issueRow(title)], [{ id: "c1", issueId: "issue_1", parentId: null, body: `see ${needle}` }])),
|
|
41
|
+
// The issue AND a comment are present in both worlds; only the body
|
|
42
|
+
// moves. A world with no comments would still fail, but for a reason
|
|
43
|
+
// closer to the empty world's than to the assertion's.
|
|
44
|
+
failing: finalWorld(linearState([issueRow(title)], [{ id: "c1", issueId: "issue_1", parentId: null, body: "nothing relevant here" }])),
|
|
45
|
+
}),
|
|
46
|
+
evaluate({ title, team, needle }, { final }) {
|
|
47
|
+
const issue = resolveIssue(final, team, title);
|
|
48
|
+
if ("missing" in issue)
|
|
49
|
+
return unresolved(issue);
|
|
50
|
+
const comments = resolveComments(final, issue.found);
|
|
51
|
+
if ("missing" in comments)
|
|
52
|
+
return unresolved(comments);
|
|
53
|
+
const hit = comments.found.some((c) => (c.body ?? "").includes(needle));
|
|
54
|
+
// Echoing the needle is safe HERE and only here: the engine skips a
|
|
55
|
+
// subject the redaction pipeline destroys before reaching this line, so
|
|
56
|
+
// anything quoted below is a string both redactors chose to leave intact.
|
|
57
|
+
return {
|
|
58
|
+
passed: hit,
|
|
59
|
+
reason: hit
|
|
60
|
+
? `a comment contains "${needle}"`
|
|
61
|
+
: `no comment contains "${needle}" (${comments.found.length} comment(s) scanned)`,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
export const issueThreadedReply = defineCheck({
|
|
66
|
+
id: "linear.issue-threaded-reply",
|
|
67
|
+
description: "Asserts a comment exists on this issue whose `parentId` names a comment that was ALREADY " +
|
|
68
|
+
"THERE IN THE SEED. Needs the seed: it is a delta, not a state assertion, and the delta is " +
|
|
69
|
+
"what separates replying inside an existing thread from posting a comment and replying to " +
|
|
70
|
+
"yourself. It asserts nothing about what the reply says, or who wrote it.",
|
|
71
|
+
template: 'A threaded reply to a seeded comment exists on issue "{title}" in `{team}`',
|
|
72
|
+
params: { title: issueTitle, team: teamKey },
|
|
73
|
+
substrate: "seed+final",
|
|
74
|
+
polarity: () => "positive",
|
|
75
|
+
// No caller-supplied literal reaches an assertion: title and team only
|
|
76
|
+
// select, and the predicate compares ids it read out of the two substrates.
|
|
77
|
+
subject: () => null,
|
|
78
|
+
// Ledgered. Both slots only SELECT, so there is nothing in the sentence to
|
|
79
|
+
// falsify — the trigger is a parentId relation between two substrates.
|
|
80
|
+
vacuityMutant: () => null,
|
|
81
|
+
discriminatingWorlds: ({ title }) => {
|
|
82
|
+
const seededRoot = { id: "root", issueId: "issue_1", parentId: null, body: "reply here" };
|
|
83
|
+
const seed = linearState([issueRow(title)], [seededRoot]);
|
|
84
|
+
return {
|
|
85
|
+
passing: deltaWorld(seed, linearState([issueRow(title)], [seededRoot, { id: "reply", issueId: "issue_1", parentId: "root", body: "on it" }])),
|
|
86
|
+
// The issue and the seeded root are present in both worlds; only the
|
|
87
|
+
// reply's PARENT moves. This agent commented — just not in the thread.
|
|
88
|
+
failing: deltaWorld(seed, linearState([issueRow(title)], [seededRoot, { id: "own", issueId: "issue_1", parentId: null, body: "on it" }])),
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
evaluate({ title, team }, { seed, final }) {
|
|
92
|
+
// The engine guards this before calling; the check guards too, so a
|
|
93
|
+
// consumer that forgets gets a named skip rather than a vacuous pass.
|
|
94
|
+
if (seed === null)
|
|
95
|
+
return { passed: false, reason: "seed_missing", status: "skipped" };
|
|
96
|
+
const seedIssue = resolveIssue(seed, team, title);
|
|
97
|
+
if ("missing" in seedIssue)
|
|
98
|
+
return unresolved(seedIssue);
|
|
99
|
+
const seedComments = resolveComments(seed, seedIssue.found);
|
|
100
|
+
if ("missing" in seedComments)
|
|
101
|
+
return unresolved(seedComments);
|
|
102
|
+
const seeded = new Set(seedComments.found.map((c) => c.id).filter((id) => typeof id === "string"));
|
|
103
|
+
const finalIssue = resolveIssue(final, team, title);
|
|
104
|
+
if ("missing" in finalIssue)
|
|
105
|
+
return unresolved(finalIssue);
|
|
106
|
+
const finalComments = resolveComments(final, finalIssue.found);
|
|
107
|
+
if ("missing" in finalComments)
|
|
108
|
+
return unresolved(finalComments);
|
|
109
|
+
const replies = finalComments.found.filter((c) => typeof c.parentId === "string" && seeded.has(c.parentId));
|
|
110
|
+
return {
|
|
111
|
+
passed: replies.length > 0,
|
|
112
|
+
reason: `${replies.length} repl${replies.length === 1 ? "y" : "ies"} to a seeded comment ` +
|
|
113
|
+
`(${seeded.size} seeded comment(s), ${finalComments.found.length} at finish)`,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=check-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-comments.js","sourceRoot":"","sources":["../../src/check-comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Check } from "./check-kind.js";
|
|
2
|
+
export declare const issueExists: Check<{
|
|
3
|
+
title: string;
|
|
4
|
+
team: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const issueState: Check<{
|
|
7
|
+
title: string;
|
|
8
|
+
team: string;
|
|
9
|
+
state: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const issueHasLabel: Check<{
|
|
12
|
+
title: string;
|
|
13
|
+
team: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const issueEstimate: Check<{
|
|
17
|
+
title: string;
|
|
18
|
+
team: string;
|
|
19
|
+
estimate: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const issueAssignee: Check<{
|
|
22
|
+
title: string;
|
|
23
|
+
team: string;
|
|
24
|
+
user: string;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// What Linear's declared checks can assert about an ISSUE ROW (F-1129).
|
|
4
|
+
//
|
|
5
|
+
// Migrated from pome-cloud's `services/evaluators/deterministic/linear.ts`,
|
|
6
|
+
// where they were hand-written regexes over a cloud-side mirror of the state
|
|
7
|
+
// shape. Three things changed in the move, and each is load-bearing:
|
|
8
|
+
//
|
|
9
|
+
// 1. THE SUBJECT IS A PARAMETER, AND IT IS THE TITLE. The legacy patterns
|
|
10
|
+
// keyed on `identifier` ("ENG-123") or `number`, and NOT ONE of the nine
|
|
11
|
+
// shipped criteria uses either — every one names a title. The registries
|
|
12
|
+
// and the tasks were built for each other and never once met. Title-keyed
|
|
13
|
+
// is what authors actually wrote, and task 26's issue does not exist in the
|
|
14
|
+
// seed at all, so it has no id to key on.
|
|
15
|
+
// 2. EVERY CHECK NAMES ITS TEAM. twin-github's repo rule, inherited rather
|
|
16
|
+
// than argued away: `seed.ts:319-325` validates issue-title uniqueness PER
|
|
17
|
+
// TEAM, so a title-keyed selector over a two-team world is exactly the
|
|
18
|
+
// ambiguity that rule closes. twin-slack could argue the absence; Linear
|
|
19
|
+
// cannot.
|
|
20
|
+
// 3. A MISSING SUBJECT FAILS. Measured on task 26: with a skip, three
|
|
21
|
+
// unresolved criteria leave the denominator and the surviving github
|
|
22
|
+
// criterion is a negative the seed already satisfies, so a do-nothing agent
|
|
23
|
+
// scores 1/1 = 100%. See `docs/grading/linear-vocabulary.md` §3. The one
|
|
24
|
+
// miss that skips is a TRUNCATED export, which the twin reports itself.
|
|
25
|
+
//
|
|
26
|
+
// `linear.issue-lifecycle` is NOT migrated. A declared `... is {lifecycle}`
|
|
27
|
+
// would near-miss every `is in state "..."` sentence and report a corrupted
|
|
28
|
+
// state criterion under the lifecycle check — pointing an author at a check
|
|
29
|
+
// they never picked, which is the exact hazard `github.issue-state`'s wording
|
|
30
|
+
// comment says its phrasing exists to avoid. It has zero corpus users, and
|
|
31
|
+
// `is in state "Done"` / `"Canceled"` re-expresses the cases that matter.
|
|
32
|
+
// Recorded as this migration's one narrowing rather than absorbed.
|
|
33
|
+
import { defineCheck, VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER } from "@pome-sh/sdk/checks";
|
|
34
|
+
import { estimatePoints, issueTitle, labelName, teamKey, userRef, workflowStateName, } from "./check-params.js";
|
|
35
|
+
import { resolveIssue, resolveLabelNames, resolveUserLabels, resolveWorkflowStateName, unresolved, } from "./check-state.js";
|
|
36
|
+
import { finalWorld, issueRow, linearState } from "./check-worlds.js";
|
|
37
|
+
export const issueExists = defineCheck({
|
|
38
|
+
id: "linear.issue-exists",
|
|
39
|
+
description: "Asserts an unarchived issue with this exact title exists in the named team. Declared and " +
|
|
40
|
+
"unused by the shipped corpus on purpose: `linear.issue-state` FAILS on a missing issue and " +
|
|
41
|
+
"therefore subsumes this one, so task 26 carries the state criterion alone — twin-github " +
|
|
42
|
+
"ships the same pair for the same reason. A vocabulary is what an author may pick from, not " +
|
|
43
|
+
"what the corpus happens to exercise. Title matching is EXACT and archived issues do not " +
|
|
44
|
+
"count, so an examinee that renames or archives the issue fails this.",
|
|
45
|
+
template: 'An issue titled "{title}" exists in `{team}`',
|
|
46
|
+
params: { title: issueTitle, team: teamKey },
|
|
47
|
+
substrate: "final",
|
|
48
|
+
polarity: () => "positive",
|
|
49
|
+
// The only check where the title IS the assertion rather than the selector,
|
|
50
|
+
// which is why it is the only one that declares the title as its subject.
|
|
51
|
+
subject: ({ title }) => title,
|
|
52
|
+
vacuityMutant: (args) => ({ ...args, title: VACUITY_SENTINEL }),
|
|
53
|
+
discriminatingWorlds: ({ title }) => ({
|
|
54
|
+
// The team is present in both worlds; only the issue moves. `linearState`
|
|
55
|
+
// always fills teams, so the failing world's reason is "no issue with that
|
|
56
|
+
// title in `ENG`" rather than the empty world's "team not found" — which is
|
|
57
|
+
// what arm 3 rejects.
|
|
58
|
+
passing: finalWorld(linearState([issueRow(title)])),
|
|
59
|
+
failing: finalWorld(linearState([issueRow("a different issue entirely")])),
|
|
60
|
+
}),
|
|
61
|
+
evaluate({ title, team }, { final }) {
|
|
62
|
+
const issue = resolveIssue(final, team, title);
|
|
63
|
+
if ("missing" in issue)
|
|
64
|
+
return unresolved(issue);
|
|
65
|
+
// Echoing the title is safe HERE and only here: this check declares it as
|
|
66
|
+
// its subject, so the engine skipped the criterion as `subject_redacted`
|
|
67
|
+
// before reaching this line if a redactor would have destroyed it.
|
|
68
|
+
return { passed: true, reason: `an issue titled "${title}" exists in \`${team}\`` };
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
export const issueState = defineCheck({
|
|
72
|
+
id: "linear.issue-state",
|
|
73
|
+
description: "Resolves the issue by title within the named team, follows `stateId` to that team's " +
|
|
74
|
+
"workflow-state row, and compares its NAME to the one given — case-insensitively, because a " +
|
|
75
|
+
"workflow state name is prose an author retypes. An issue that is absent, archived, or " +
|
|
76
|
+
"ambiguous FAILS, which is what makes this check subsume the existence assertion. A miss " +
|
|
77
|
+
"inside a TRUNCATED export skips instead, because the twin reported that rows were dropped. " +
|
|
78
|
+
"Workflow state names are user-defined per team, so the slot is free text rather than a " +
|
|
79
|
+
"closed set.",
|
|
80
|
+
// "is in state X", not "is X". Two reasons, both of which twin-github's
|
|
81
|
+
// `issue-state` comment states while crediting this very idiom to the Linear
|
|
82
|
+
// tasks: one reading habit spans twins, and the longer literal tail keeps
|
|
83
|
+
// this template from near-missing `... is assigned to \`{user}\``.
|
|
84
|
+
template: 'Issue "{title}" in `{team}` is in state "{state}"',
|
|
85
|
+
params: { title: issueTitle, team: teamKey, state: workflowStateName },
|
|
86
|
+
substrate: "final",
|
|
87
|
+
// Positive on every shipped use. Unlike `github.issue-state` there is no
|
|
88
|
+
// canonical "open" member to read a prohibition off, because the state names
|
|
89
|
+
// belong to the workspace rather than to the API.
|
|
90
|
+
polarity: () => "positive",
|
|
91
|
+
// The state name is the value COMPARED against the state; title and team only
|
|
92
|
+
// select.
|
|
93
|
+
subject: ({ state }) => state,
|
|
94
|
+
vacuityMutant: (args) => ({ ...args, state: VACUITY_SENTINEL }),
|
|
95
|
+
discriminatingWorlds: ({ title, state }) => ({
|
|
96
|
+
passing: finalWorld(linearState([issueRow(title, { stateName: state })])),
|
|
97
|
+
// The team and the issue are PRESENT in both worlds; only the state moves.
|
|
98
|
+
failing: finalWorld(linearState([issueRow(title, { stateName: "Backlog" })])),
|
|
99
|
+
}),
|
|
100
|
+
evaluate({ title, team, state }, { final }) {
|
|
101
|
+
const issue = resolveIssue(final, team, title);
|
|
102
|
+
if ("missing" in issue)
|
|
103
|
+
return unresolved(issue);
|
|
104
|
+
const name = resolveWorkflowStateName(final, issue.found);
|
|
105
|
+
if ("missing" in name)
|
|
106
|
+
return unresolved(name);
|
|
107
|
+
return {
|
|
108
|
+
passed: name.found.toLowerCase() === state.toLowerCase(),
|
|
109
|
+
reason: `issue state is "${name.found}" (wanted "${state}")`,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
export const issueHasLabel = defineCheck({
|
|
114
|
+
id: "linear.issue-has-label",
|
|
115
|
+
description: "Resolves the issue, joins its `labelIds` to the workspace label catalog, and asserts the " +
|
|
116
|
+
"named label is among them — case-insensitively, as the legacy rule's comparison was. The " +
|
|
117
|
+
"join is the point: this export carries label IDS where the seed writes names and " +
|
|
118
|
+
"twin-github writes objects, so one concept has three shapes and only this one is exported. " +
|
|
119
|
+
"A label id with no catalog row is a partial export and SKIPS rather than failing.",
|
|
120
|
+
template: 'Issue "{title}" in `{team}` has label "{label}"',
|
|
121
|
+
params: { title: issueTitle, team: teamKey, label: labelName },
|
|
122
|
+
substrate: "final",
|
|
123
|
+
polarity: () => "positive",
|
|
124
|
+
subject: ({ label }) => label,
|
|
125
|
+
vacuityMutant: (args) => ({ ...args, label: VACUITY_SENTINEL }),
|
|
126
|
+
discriminatingWorlds: ({ title, label }) => ({
|
|
127
|
+
passing: finalWorld(linearState([issueRow(title, { labelNames: [label] })], [], [label])),
|
|
128
|
+
// The issue is present and resolvable in both; only its labels move.
|
|
129
|
+
failing: finalWorld(linearState([issueRow(title, { labelNames: [] })], [], [label])),
|
|
130
|
+
}),
|
|
131
|
+
evaluate({ title, team, label }, { final }) {
|
|
132
|
+
const issue = resolveIssue(final, team, title);
|
|
133
|
+
if ("missing" in issue)
|
|
134
|
+
return unresolved(issue);
|
|
135
|
+
const names = resolveLabelNames(final, issue.found);
|
|
136
|
+
if ("missing" in names)
|
|
137
|
+
return unresolved(names);
|
|
138
|
+
return {
|
|
139
|
+
passed: names.found.has(label.toLowerCase()),
|
|
140
|
+
reason: `issue carries ${names.found.size} label(s) (wanted "${label}")`,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
export const issueEstimate = defineCheck({
|
|
145
|
+
id: "linear.issue-estimate",
|
|
146
|
+
description: "Resolves the issue and compares its `estimate` column to the number given. An UNSET " +
|
|
147
|
+
"estimate is a real FAIL, not a skip: an unestimated issue is exactly the state this " +
|
|
148
|
+
"assertion exists to rule out.",
|
|
149
|
+
template: 'Issue "{title}" in `{team}` has estimate {estimate}',
|
|
150
|
+
params: { title: issueTitle, team: teamKey, estimate: estimatePoints },
|
|
151
|
+
substrate: "final",
|
|
152
|
+
polarity: () => "positive",
|
|
153
|
+
// The single null in the subject column, and for twin-slack's emoji-name
|
|
154
|
+
// reason rather than an exemption: a bare integer of at most three digits
|
|
155
|
+
// matches no pattern in `redactSecrets` (key prefixes, 13-19-digit runs) or
|
|
156
|
+
// in a team's `PII_PATTERNS` (emails, phones). A value no redactor can eat is
|
|
157
|
+
// not a subject, and declaring one would only narrow the corpus gate's
|
|
158
|
+
// whole-phrase fallback for nothing.
|
|
159
|
+
subject: () => null,
|
|
160
|
+
// D10's second allowlist entry ever, and the argument is stripe's unchanged:
|
|
161
|
+
// here the number IS the scanned value, and the title is the selector.
|
|
162
|
+
vacuityMutant: (args) => ({ ...args, estimate: String(VACUITY_SENTINEL_NUMBER) }),
|
|
163
|
+
discriminatingWorlds: ({ title, estimate }) => ({
|
|
164
|
+
passing: finalWorld(linearState([issueRow(title, { estimate: Number(estimate) })])),
|
|
165
|
+
failing: finalWorld(linearState([issueRow(title, { estimate: null })])),
|
|
166
|
+
}),
|
|
167
|
+
evaluate({ title, team, estimate }, { final }) {
|
|
168
|
+
const issue = resolveIssue(final, team, title);
|
|
169
|
+
if ("missing" in issue)
|
|
170
|
+
return unresolved(issue);
|
|
171
|
+
const actual = issue.found.estimate ?? null;
|
|
172
|
+
return {
|
|
173
|
+
passed: actual === Number(estimate),
|
|
174
|
+
reason: `issue estimate is ${actual === null ? "unset" : actual} (wanted ${estimate})`,
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
export const issueAssignee = defineCheck({
|
|
179
|
+
id: "linear.issue-assignee",
|
|
180
|
+
description: "Resolves the issue, then its assignee, and matches the given reference against that user's " +
|
|
181
|
+
"email, name OR displayName — every spelling the legacy rule accepted. An UNASSIGNED issue " +
|
|
182
|
+
"is a real FAIL. Declared with no shipped corpus user, carrying a legacy capability forward. " +
|
|
183
|
+
"This is the check whose `subject` earns its keep: an email reference is destroyed by a " +
|
|
184
|
+
"team's `PII_PATTERNS`, which the twin's own redactor has no equivalent of, so without the " +
|
|
185
|
+
"declaration the criterion would silently be unable to fire.",
|
|
186
|
+
template: 'Issue "{title}" in `{team}` is assigned to `{user}`',
|
|
187
|
+
params: { title: issueTitle, team: teamKey, user: userRef },
|
|
188
|
+
substrate: "final",
|
|
189
|
+
polarity: () => "positive",
|
|
190
|
+
subject: ({ user }) => user,
|
|
191
|
+
vacuityMutant: (args) => ({ ...args, user: VACUITY_SENTINEL }),
|
|
192
|
+
discriminatingWorlds: ({ title }) => ({
|
|
193
|
+
// The issue resolves in both worlds; only the assignee moves.
|
|
194
|
+
passing: finalWorld(linearState([issueRow(title, { assigneeId: "user_dev" })])),
|
|
195
|
+
failing: finalWorld(linearState([issueRow(title)])),
|
|
196
|
+
}),
|
|
197
|
+
evaluate({ title, team, user }, { final }) {
|
|
198
|
+
const issue = resolveIssue(final, team, title);
|
|
199
|
+
if ("missing" in issue)
|
|
200
|
+
return unresolved(issue);
|
|
201
|
+
const labels = resolveUserLabels(final, issue.found.assigneeId);
|
|
202
|
+
if (labels.length === 0)
|
|
203
|
+
return { passed: false, reason: "issue has no assignee" };
|
|
204
|
+
return {
|
|
205
|
+
passed: labels.some((l) => l.toLowerCase() === user.toLowerCase()),
|
|
206
|
+
// Safe to quote: `user` is this check's declared subject.
|
|
207
|
+
reason: `issue is assigned to \`${labels[0]}\` (wanted \`${user}\`)`,
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
//# sourceMappingURL=check-issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-issues.js","sourceRoot":"","sources":["../../src/check-issues.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AACxE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,+CAA+C;AAC/C,6EAA6E;AAC7E,gFAAgF;AAChF,4EAA4E;AAC5E,8EAA8E;AAC9E,eAAe;AACf,wEAAwE;AACxE,0EAA0E;AAC1E,iFAAiF;AACjF,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAC1E,mEAAmE;AAEnE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE7F,OAAO,EACL,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,CAAC,MAAM,WAAW,GAA2C,WAAW,CAAC;IAC7E,EAAE,EAAE,qBAAqB;IACzB,WAAW,EACT,2FAA2F;QAC3F,6FAA6F;QAC7F,0FAA0F;QAC1F,6FAA6F;QAC7F,0FAA0F;QAC1F,sEAAsE;IACxE,QAAQ,EAAE,8CAA8C;IACxD,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE;IAC5C,SAAS,EAAE,OAAO;IAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,4EAA4E;IAC5E,0EAA0E;IAC1E,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK;IAC7B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC/D,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACpC,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,sBAAsB;QACtB,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;KAC3E,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE;QACjC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,0EAA0E;QAC1E,yEAAyE;QACzE,mEAAmE;QACnE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,oBAAoB,KAAK,iBAAiB,IAAI,IAAI,EAAE,CAAC;IACtF,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAA0D,WAAW,CAAC;IAC3F,EAAE,EAAE,oBAAoB;IACxB,WAAW,EACT,sFAAsF;QACtF,6FAA6F;QAC7F,wFAAwF;QACxF,0FAA0F;QAC1F,6FAA6F;QAC7F,yFAAyF;QACzF,aAAa;IACf,wEAAwE;IACxE,6EAA6E;IAC7E,0EAA0E;IAC1E,mEAAmE;IACnE,QAAQ,EAAE,mDAAmD;IAC7D,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACtE,SAAS,EAAE,OAAO;IAClB,yEAAyE;IACzE,6EAA6E;IAC7E,kDAAkD;IAClD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,8EAA8E;IAC9E,UAAU;IACV,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK;IAC7B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC/D,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACzE,2EAA2E;QAC3E,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;KAC9E,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE;QACxC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1D,IAAI,SAAS,IAAI,IAAI;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/C,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE;YACxD,MAAM,EAAE,mBAAmB,IAAI,CAAC,KAAK,cAAc,KAAK,IAAI;SAC7D,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAA0D,WAAW,CAAC;IAC9F,EAAE,EAAE,wBAAwB;IAC5B,WAAW,EACT,2FAA2F;QAC3F,2FAA2F;QAC3F,mFAAmF;QACnF,6FAA6F;QAC7F,mFAAmF;IACrF,QAAQ,EAAE,iDAAiD;IAC3D,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;IAC9D,SAAS,EAAE,OAAO;IAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK;IAC7B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC/D,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACzF,qEAAqE;QACrE,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;KACrF,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE;QACxC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,OAAO;YACL,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,EAAE,iBAAiB,KAAK,CAAC,KAAK,CAAC,IAAI,sBAAsB,KAAK,IAAI;SACzE,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAA6D,WAAW,CAAC;IACjG,EAAE,EAAE,uBAAuB;IAC3B,WAAW,EACT,sFAAsF;QACtF,sFAAsF;QACtF,+BAA+B;IACjC,QAAQ,EAAE,qDAAqD;IAC/D,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE;IACtE,SAAS,EAAE,OAAO;IAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,8EAA8E;IAC9E,uEAAuE;IACvE,qCAAqC;IACrC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;IACnB,6EAA6E;IAC7E,uEAAuE;IACvE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,uBAAuB,CAAC,EAAE,CAAC;IACjF,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9C,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnF,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;KACxE,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE;QAC3C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;QAC5C,OAAO;YACL,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC;YACnC,MAAM,EAAE,qBAAqB,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,YAAY,QAAQ,GAAG;SACvF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAyD,WAAW,CAAC;IAC7F,EAAE,EAAE,uBAAuB;IAC3B,WAAW,EACT,6FAA6F;QAC7F,4FAA4F;QAC5F,8FAA8F;QAC9F,yFAAyF;QACzF,4FAA4F;QAC5F,6DAA6D;IAC/D,QAAQ,EAAE,qDAAqD;IAC/D,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;IAC3D,SAAS,EAAE,OAAO;IAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;IAC1B,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI;IAC3B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAC9D,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACpC,8DAA8D;QAC9D,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/E,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACpD,CAAC;IACF,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE;QACvC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,KAAK;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QACnF,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YAClE,0DAA0D;YAC1D,MAAM,EAAE,0BAA0B,MAAM,CAAC,CAAC,CAAC,gBAAgB,IAAI,KAAK;SACrE,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// The one type every Linear check declaration is written against, mirroring
|
|
4
|
+
// twin-github's and twin-slack's. It exists so the declaration files can split
|
|
5
|
+
// by what they assert about without each re-deriving the binding to the state
|
|
6
|
+
// tree — and so a declaration that forgets to name `LinearCheckState` cannot
|
|
7
|
+
// compile.
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=check-kind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-kind.js","sourceRoot":"","sources":["../../src/check-kind.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,6EAA6E;AAC7E,WAAW"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CheckParamType } from "@pome-sh/sdk/checks";
|
|
2
|
+
export declare const issueTitle: CheckParamType;
|
|
3
|
+
export declare const teamKey: CheckParamType;
|
|
4
|
+
export declare const workflowStateName: CheckParamType;
|
|
5
|
+
export declare const labelName: CheckParamType;
|
|
6
|
+
export declare const estimatePoints: CheckParamType;
|
|
7
|
+
export declare const userRef: CheckParamType;
|
|
8
|
+
export declare const commentNeedle: CheckParamType;
|