@pome-sh/cli 0.15.0 → 0.17.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-add.d.ts +2 -7
- package/dist/src/cli/checks-add.js +10 -24
- package/dist/src/cli/checks.d.ts +3 -2
- package/dist/src/cli/checks.js +22 -9
- package/dist/src/cli/vocabulary-skew.d.ts +64 -0
- package/dist/src/cli/vocabulary-skew.js +187 -0
- package/dist/src/twin/githubCloneAdapter.d.ts +1 -0
- package/node_modules/@pome-sh/twin-github/FIDELITY.md +14 -0
- package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.d.ts +4 -0
- package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js +93 -0
- package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-state.d.ts +1 -0
- package/node_modules/@pome-sh/twin-github/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/checks.d.ts +3 -0
- package/node_modules/@pome-sh/twin-github/dist/src/checks.js +2 -1
- package/node_modules/@pome-sh/twin-github/dist/src/checks.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/db.js +46 -4
- package/node_modules/@pome-sh/twin-github/dist/src/db.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/domain/github-domain.d.ts +3 -0
- package/node_modules/@pome-sh/twin-github/dist/src/domain/github-domain.js +49 -1
- package/node_modules/@pome-sh/twin-github/dist/src/domain/github-domain.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/domain/issues.js +15 -5
- package/node_modules/@pome-sh/twin-github/dist/src/domain/issues.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/serializers.d.ts +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/serializers.js +8 -2
- package/node_modules/@pome-sh/twin-github/dist/src/serializers.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/package.json +1 -1
- package/node_modules/@pome-sh/twin-linear/CHANGELOG.md +142 -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/db.d.ts +6 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/db.js +53 -4
- package/node_modules/@pome-sh/twin-linear/dist/src/db.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/agents.d.ts +16 -13
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/agents.js +16 -12
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/agents.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/comments.js +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/comments.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/documents.js +8 -8
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/documents.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/issues.js +20 -17
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/issues.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/labels.js +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/labels.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/linear-domain.d.ts +4 -17
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/linear-domain.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/normalize.d.ts +12 -2
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/normalize.js +28 -5
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/normalize.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/projects-cycles.js +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/projects-cycles.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/rows.d.ts +3 -3
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/rows.js +4 -3
- package/node_modules/@pome-sh/twin-linear/dist/src/domain/rows.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/formatters.js +3 -3
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/formatters.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/mutation-inputs.d.ts +30 -12
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/mutation-inputs.js +33 -28
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/mutation-inputs.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/resolvers.js +2 -2
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/resolvers.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/schema.js +31 -13
- package/node_modules/@pome-sh/twin-linear/dist/src/graphql/schema.js.map +1 -1
- 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/dist/src/state.js +4 -4
- package/node_modules/@pome-sh/twin-linear/dist/src/state.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/types.d.ts +10 -4
- package/node_modules/@pome-sh/twin-linear/dist/src/types.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/fixtures/README.md +1 -0
- package/node_modules/@pome-sh/twin-linear/fixtures/linear-introspection.json +66 -0
- package/node_modules/@pome-sh/twin-linear/package.json +2 -1
- package/node_modules/@pome-sh/twin-stripe/FIDELITY.md +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/idempotency.d.ts +23 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/idempotency.js +40 -6
- package/node_modules/@pome-sh/twin-stripe/dist/src/idempotency.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js +5 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/twin.js +7 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/twin.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/package.json +1 -1
- package/package.json +4 -4
- package/tasks/14-stripe-refund-retry.md +17 -14
- 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
|
@@ -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":"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"}
|
|
@@ -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;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// The typed slots Linear's declared checks fill (F-1129).
|
|
4
|
+
//
|
|
5
|
+
// They live in the twin, not the sdk, for the same reason the declarations do:
|
|
6
|
+
// the twin owns what a Linear team key or workflow state name may look like.
|
|
7
|
+
// Every pattern is capture-group-free — `defineCheck` throws otherwise, because
|
|
8
|
+
// each consumer reads capture group i+1 as slot i and one smuggled group hands
|
|
9
|
+
// every predicate its neighbour's argument.
|
|
10
|
+
//
|
|
11
|
+
// NO SLOT MAY SIT AT THE END OF A TEMPLATE UNBOUNDED. The contract suite
|
|
12
|
+
// requires that `"${rendered} and also something else"` does not match, and an
|
|
13
|
+
// anchored `[^"\n]+` in terminal position would swallow the suffix. Every
|
|
14
|
+
// free-text slot below is delimited by a quote or a backtick in the templates
|
|
15
|
+
// that use it — which is also why Linear's state slot is quoted where
|
|
16
|
+
// `github.issue-state`'s is bare.
|
|
17
|
+
// Double-quote delimited. Linear puts no constraint on an issue title beyond
|
|
18
|
+
// non-empty, so the quote is the only exclusion.
|
|
19
|
+
export const issueTitle = {
|
|
20
|
+
name: "title",
|
|
21
|
+
pattern: '[^"\\n]+',
|
|
22
|
+
example: "Orders 500 after deploy",
|
|
23
|
+
render: (value) => value,
|
|
24
|
+
parse: (raw) => raw,
|
|
25
|
+
};
|
|
26
|
+
// Linear's own team-key constraint, from `seedSchema` (`seed.ts`):
|
|
27
|
+
// /^[A-Z][A-Z0-9]*$/.
|
|
28
|
+
//
|
|
29
|
+
// This is the SCOPE slot twin-github's repo rule requires of every state check,
|
|
30
|
+
// and Linear inherits that rule rather than arguing it away as twin-slack did:
|
|
31
|
+
// `seed.ts:319-325` validates issue-title uniqueness PER TEAM, so a title-keyed
|
|
32
|
+
// selector over a two-team world is exactly the ambiguity the rule closes.
|
|
33
|
+
//
|
|
34
|
+
// Its shape is also why a reason string may quote it — an uppercase
|
|
35
|
+
// alphanumeric run matches no pattern in either redactor.
|
|
36
|
+
export const teamKey = {
|
|
37
|
+
name: "team",
|
|
38
|
+
pattern: "[A-Z][A-Z0-9]*",
|
|
39
|
+
example: "ENG",
|
|
40
|
+
render: (value) => value,
|
|
41
|
+
parse: (raw) => raw,
|
|
42
|
+
};
|
|
43
|
+
// NOT a closed set, where `github.issue-state`'s is. GitHub has exactly two
|
|
44
|
+
// issue states; Linear workflow state names are user-defined per team
|
|
45
|
+
// (`seed.ts` `teams[].states[].name` is free text), so a closed alternation
|
|
46
|
+
// would silently exclude every custom workflow. The template quotes it, which
|
|
47
|
+
// is what keeps a free-text slot safe in terminal position.
|
|
48
|
+
export const workflowStateName = {
|
|
49
|
+
name: "state",
|
|
50
|
+
pattern: '[^"\\n]+',
|
|
51
|
+
example: "In Progress",
|
|
52
|
+
render: (value) => value,
|
|
53
|
+
parse: (raw) => raw,
|
|
54
|
+
};
|
|
55
|
+
export const labelName = {
|
|
56
|
+
name: "label",
|
|
57
|
+
pattern: '[^"\\n]+',
|
|
58
|
+
example: "Agent",
|
|
59
|
+
render: (value) => value,
|
|
60
|
+
parse: (raw) => raw,
|
|
61
|
+
};
|
|
62
|
+
// A bare integer, no leading zeros, so `render(parse(x))` round-trips.
|
|
63
|
+
// `VACUITY_SENTINEL_NUMBER` (987654321) satisfies this pattern, which is what
|
|
64
|
+
// lets this slot carry a real mutant rather than an admitted null.
|
|
65
|
+
export const estimatePoints = {
|
|
66
|
+
name: "estimate",
|
|
67
|
+
pattern: "0|[1-9][0-9]*",
|
|
68
|
+
example: "2",
|
|
69
|
+
render: (value) => value,
|
|
70
|
+
parse: (raw) => raw,
|
|
71
|
+
};
|
|
72
|
+
// Backtick-delimited, matching `github.issue-assignee`'s `{login}`. The legacy
|
|
73
|
+
// rule matched a user by email, name OR displayName and this keeps all three —
|
|
74
|
+
// which is exactly why its check declares this as its `subject`: the team's
|
|
75
|
+
// `PII_PATTERNS` eats an email that the twin's own `SCRUB_STEPS` leaves alone,
|
|
76
|
+
// so without the declaration the criterion could never fire.
|
|
77
|
+
export const userRef = {
|
|
78
|
+
name: "user",
|
|
79
|
+
pattern: "[^`\\n]+",
|
|
80
|
+
example: "dev@pome-twin.test",
|
|
81
|
+
render: (value) => value,
|
|
82
|
+
parse: (raw) => raw,
|
|
83
|
+
};
|
|
84
|
+
// The value SCANNED inside free prose rather than compared to a field, which is
|
|
85
|
+
// why its check declares it as `subject` — twin-slack's `messageNeedle`
|
|
86
|
+
// precedent.
|
|
87
|
+
export const commentNeedle = {
|
|
88
|
+
name: "needle",
|
|
89
|
+
pattern: '[^"\\n]+',
|
|
90
|
+
example: "triage",
|
|
91
|
+
render: (value) => value,
|
|
92
|
+
parse: (raw) => raw,
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=check-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-params.js","sourceRoot":"","sources":["../../src/check-params.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,4CAA4C;AAC5C,EAAE;AACF,yEAAyE;AACzE,+EAA+E;AAC/E,0EAA0E;AAC1E,8EAA8E;AAC9E,sEAAsE;AACtE,kCAAkC;AAIlC,6EAA6E;AAC7E,iDAAiD;AACjD,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,yBAAyB;IAClC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,mEAAmE;AACnE,sBAAsB;AACtB,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,2EAA2E;AAC3E,EAAE;AACF,oEAAoE;AACpE,0DAA0D;AAC1D,MAAM,CAAC,MAAM,OAAO,GAAmB;IACrC,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,gBAAgB;IACzB,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,4EAA4E;AAC5E,sEAAsE;AACtE,4EAA4E;AAC5E,8EAA8E;AAC9E,4DAA4D;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAmB;IAC/C,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,aAAa;IACtB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,uEAAuE;AACvE,8EAA8E;AAC9E,mEAAmE;AACnE,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,eAAe;IACxB,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAC/E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,OAAO,GAAmB;IACrC,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,oBAAoB;IAC7B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC;AAEF,gFAAgF;AAChF,wEAAwE;AACxE,aAAa;AACb,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;CACpB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { CheckOutcome } from "@pome-sh/sdk/checks";
|
|
2
|
+
export interface LinearCheckStateTeam {
|
|
3
|
+
id?: string;
|
|
4
|
+
key?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LinearCheckStateWorkflowState {
|
|
8
|
+
id?: string;
|
|
9
|
+
teamId?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
type?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface LinearCheckStateLabel {
|
|
14
|
+
id?: string;
|
|
15
|
+
teamId?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface LinearCheckStateIssue {
|
|
19
|
+
id?: string;
|
|
20
|
+
identifier?: string;
|
|
21
|
+
number?: number;
|
|
22
|
+
teamId?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
estimate?: number | null;
|
|
25
|
+
stateId?: string;
|
|
26
|
+
assigneeId?: string;
|
|
27
|
+
archivedAt?: string | null;
|
|
28
|
+
labelIds?: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface LinearCheckStateComment {
|
|
31
|
+
id?: string;
|
|
32
|
+
issueId?: string;
|
|
33
|
+
parentId?: string | null;
|
|
34
|
+
userId?: string;
|
|
35
|
+
body?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface LinearCheckStateUser {
|
|
38
|
+
id?: string;
|
|
39
|
+
email?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
displayName?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface LinearCheckState {
|
|
44
|
+
teams?: LinearCheckStateTeam[] | null;
|
|
45
|
+
workflowStates?: LinearCheckStateWorkflowState[] | null;
|
|
46
|
+
labels?: LinearCheckStateLabel[] | null;
|
|
47
|
+
issues?: LinearCheckStateIssue[] | null;
|
|
48
|
+
comments?: LinearCheckStateComment[] | null;
|
|
49
|
+
users?: LinearCheckStateUser[] | null;
|
|
50
|
+
exportBounds?: {
|
|
51
|
+
truncatedCollections?: string[];
|
|
52
|
+
} | null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* twin-github's `Resolved<T>` with one field added.
|
|
56
|
+
*
|
|
57
|
+
* github's resolvers always FAIL on a miss and twin-slack's always SKIP. Linear
|
|
58
|
+
* needs both from the same resolver, because a miss inside a truncated
|
|
59
|
+
* collection is evidence of a partial export while a miss in a complete one is
|
|
60
|
+
* evidence about the examinee. Carrying the disposition on the value is what
|
|
61
|
+
* stops every call site from re-deciding it — and re-deciding it wrongly is how
|
|
62
|
+
* a do-nothing agent scores 100% on task 26.
|
|
63
|
+
*/
|
|
64
|
+
export type Resolved<T> = {
|
|
65
|
+
found: T;
|
|
66
|
+
} | {
|
|
67
|
+
missing: string;
|
|
68
|
+
skip: boolean;
|
|
69
|
+
};
|
|
70
|
+
/** The one place an unresolved selector becomes an outcome. */
|
|
71
|
+
export declare function unresolved(r: {
|
|
72
|
+
missing: string;
|
|
73
|
+
skip: boolean;
|
|
74
|
+
}): CheckOutcome;
|
|
75
|
+
export declare function isTruncated(state: LinearCheckState, collection: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The issue an assertion is about, by exact title, scoped to one team.
|
|
78
|
+
*
|
|
79
|
+
* Exact rather than case-insensitive: the task names the title it seeded or
|
|
80
|
+
* asked for, and a case-folding match would let an examinee half-comply.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveIssue(state: LinearCheckState, teamKey: string, title: string): Resolved<LinearCheckStateIssue>;
|
|
83
|
+
/** Trap 1: `stateId` → the team's own workflow-state row. */
|
|
84
|
+
export declare function resolveWorkflowStateName(state: LinearCheckState, issue: LinearCheckStateIssue): Resolved<string>;
|
|
85
|
+
/**
|
|
86
|
+
* Trap 2: `labelIds` → catalog names, lowercased.
|
|
87
|
+
*
|
|
88
|
+
* Case-insensitive because the legacy rule's `eqi` was, and because a label an
|
|
89
|
+
* examinee creates is prose it retyped.
|
|
90
|
+
*/
|
|
91
|
+
export declare function resolveLabelNames(state: LinearCheckState, issue: LinearCheckStateIssue): Resolved<Set<string>>;
|
|
92
|
+
export declare function resolveComments(state: LinearCheckState, issue: LinearCheckStateIssue): Resolved<LinearCheckStateComment[]>;
|
|
93
|
+
/**
|
|
94
|
+
* Every spelling of a user the legacy `linear.issue-assignee` rule accepted —
|
|
95
|
+
* email, name, displayName — in that order, so a reason string names the most
|
|
96
|
+
* specific one first.
|
|
97
|
+
*/
|
|
98
|
+
export declare function resolveUserLabels(state: LinearCheckState, id: string | undefined): string[];
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// How a declared check READS the exported Linear workspace (F-1129).
|
|
4
|
+
//
|
|
5
|
+
// `LinearStateExport` (`state.ts:4-27`) names the collections but types every
|
|
6
|
+
// row `unknown[]`, so the row shapes still have to be modelled by hand here, as
|
|
7
|
+
// twin-slack's `check-state.ts` does. Every field is optional and every list
|
|
8
|
+
// nullable: an older snapshot, a partial upload, or a schema that gained a
|
|
9
|
+
// column all arrive as absent fields. A predicate that assumes presence throws
|
|
10
|
+
// inside the evaluator; one that reads this model returns a NAMED verdict. That
|
|
11
|
+
// is the D4 bargain — a criterion may leave the denominator, but it may never
|
|
12
|
+
// fabricate one.
|
|
13
|
+
//
|
|
14
|
+
// Five shapes are counter-intuitive and each has a wrong-verdict story:
|
|
15
|
+
//
|
|
16
|
+
// 1. AN ISSUE HAS NO STATE STRING. `issue.stateId` references a
|
|
17
|
+
// `workflowStates` row, and workflow states are scoped by `teamId` — so a
|
|
18
|
+
// join that is not team-scoped resolves the wrong row in a two-team world.
|
|
19
|
+
// 2. LABELS ARE A THIRD SHAPE. The SEED writes label names; twin-github's
|
|
20
|
+
// export writes label objects; this export writes `issue.labelIds:
|
|
21
|
+
// string[]`, which must be joined to `labels[].name`. One concept, three
|
|
22
|
+
// shapes, and only this one is exported.
|
|
23
|
+
// 3. `archivedAt` IS EXPORTED. An archived issue is still a row in `issues`.
|
|
24
|
+
// Resolution treats archived as absent, so an existence assertion cannot
|
|
25
|
+
// be satisfied by an issue the examinee archived.
|
|
26
|
+
// 4. `exportBounds.truncatedCollections` NAMES WHAT WAS CAPPED.
|
|
27
|
+
// `STATE_EXPORT_CAP = 2000` (`types.ts:394`) drops rows past the cap, and
|
|
28
|
+
// this is the ONLY place in the vocabulary where "not found" and "absent"
|
|
29
|
+
// are different facts. Neither GitHubCheckState nor SlackCheckState
|
|
30
|
+
// carries an analogue, which is why this contract is Linear's alone.
|
|
31
|
+
// 5. TITLE UNIQUENESS IS SEED-TIME ONLY. `seed.ts:319-325` validates
|
|
32
|
+
// `issueTitlesByTeam` at parse; nothing enforces it at runtime, so an
|
|
33
|
+
// examinee can create a duplicate. Two matches FAIL naming the count —
|
|
34
|
+
// silently grading the first is the wrong-match hazard twin-github's repo
|
|
35
|
+
// rule exists to close, one level down.
|
|
36
|
+
//
|
|
37
|
+
// REASON STRINGS NEVER ECHO A TITLE. Only a check's declared `subject` is
|
|
38
|
+
// guaranteed to have survived the redaction pipeline by the time `evaluate`
|
|
39
|
+
// runs (`evaluate.ts:141-144`), and `title` is a selector on every check except
|
|
40
|
+
// `linear.issue-exists`. The team key is safe to quote: `[A-Z][A-Z0-9]*`
|
|
41
|
+
// matches no pattern in either redactor.
|
|
42
|
+
/** The one place an unresolved selector becomes an outcome. */
|
|
43
|
+
export function unresolved(r) {
|
|
44
|
+
return r.skip
|
|
45
|
+
? { passed: false, status: "skipped", reason: r.missing }
|
|
46
|
+
: { passed: false, reason: r.missing };
|
|
47
|
+
}
|
|
48
|
+
export function isTruncated(state, collection) {
|
|
49
|
+
return (state.exportBounds?.truncatedCollections ?? []).includes(collection);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The issue an assertion is about, by exact title, scoped to one team.
|
|
53
|
+
*
|
|
54
|
+
* Exact rather than case-insensitive: the task names the title it seeded or
|
|
55
|
+
* asked for, and a case-folding match would let an examinee half-comply.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveIssue(state, teamKey, title) {
|
|
58
|
+
if (state.teams == null)
|
|
59
|
+
return { missing: "state_incomplete", skip: true };
|
|
60
|
+
const team = state.teams.find((t) => t.key === teamKey);
|
|
61
|
+
if (team?.id == null) {
|
|
62
|
+
return { missing: `team \`${teamKey}\` not found in state_final`, skip: false };
|
|
63
|
+
}
|
|
64
|
+
if (state.issues == null)
|
|
65
|
+
return { missing: "state_incomplete", skip: true };
|
|
66
|
+
const matches = state.issues.filter((issue) => issue.teamId === team.id &&
|
|
67
|
+
issue.title === title &&
|
|
68
|
+
(issue.archivedAt ?? null) === null);
|
|
69
|
+
if (matches.length === 1)
|
|
70
|
+
return { found: matches[0] };
|
|
71
|
+
if (matches.length > 1) {
|
|
72
|
+
return {
|
|
73
|
+
missing: `${matches.length} issues in \`${teamKey}\` share that title`,
|
|
74
|
+
skip: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// The only skip a miss can earn, and it is earned by evidence rather than by
|
|
78
|
+
// taste: the twin itself reported that `issues` lost rows to the export cap.
|
|
79
|
+
if (isTruncated(state, "issues"))
|
|
80
|
+
return { missing: "state_truncated", skip: true };
|
|
81
|
+
return { missing: `no issue with that title in \`${teamKey}\``, skip: false };
|
|
82
|
+
}
|
|
83
|
+
/** Trap 1: `stateId` → the team's own workflow-state row. */
|
|
84
|
+
export function resolveWorkflowStateName(state, issue) {
|
|
85
|
+
if (state.workflowStates == null)
|
|
86
|
+
return { missing: "state_incomplete", skip: true };
|
|
87
|
+
const row = state.workflowStates.find((s) => s.id === issue.stateId && s.teamId === issue.teamId);
|
|
88
|
+
if (row?.name == null)
|
|
89
|
+
return { missing: "workflow_state_unresolved", skip: true };
|
|
90
|
+
return { found: row.name };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Trap 2: `labelIds` → catalog names, lowercased.
|
|
94
|
+
*
|
|
95
|
+
* Case-insensitive because the legacy rule's `eqi` was, and because a label an
|
|
96
|
+
* examinee creates is prose it retyped.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveLabelNames(state, issue) {
|
|
99
|
+
if (state.labels == null)
|
|
100
|
+
return { missing: "state_incomplete", skip: true };
|
|
101
|
+
const byId = new Map(state.labels.filter((l) => l.id != null).map((l) => [l.id, (l.name ?? "").toLowerCase()]));
|
|
102
|
+
const names = new Set();
|
|
103
|
+
for (const id of issue.labelIds ?? []) {
|
|
104
|
+
const name = byId.get(id);
|
|
105
|
+
// A link to a label the export did not carry is a partial export, not a
|
|
106
|
+
// verdict about the examinee.
|
|
107
|
+
if (name === undefined)
|
|
108
|
+
return { missing: "label_unresolved", skip: true };
|
|
109
|
+
names.add(name);
|
|
110
|
+
}
|
|
111
|
+
return { found: names };
|
|
112
|
+
}
|
|
113
|
+
export function resolveComments(state, issue) {
|
|
114
|
+
if (state.comments == null)
|
|
115
|
+
return { missing: "state_incomplete", skip: true };
|
|
116
|
+
return { found: state.comments.filter((c) => c.issueId === issue.id) };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Every spelling of a user the legacy `linear.issue-assignee` rule accepted —
|
|
120
|
+
* email, name, displayName — in that order, so a reason string names the most
|
|
121
|
+
* specific one first.
|
|
122
|
+
*/
|
|
123
|
+
export function resolveUserLabels(state, id) {
|
|
124
|
+
if (id == null || state.users == null)
|
|
125
|
+
return [];
|
|
126
|
+
const user = state.users.find((u) => u.id === id);
|
|
127
|
+
if (user == null)
|
|
128
|
+
return [];
|
|
129
|
+
return [user.email, user.name, user.displayName].filter((v) => typeof v === "string" && v.length > 0);
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=check-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-state.js","sourceRoot":"","sources":["../../src/check-state.ts"],"names":[],"mappings":"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"}
|