@pome-sh/cli 0.14.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 +15 -0
- package/dist/src/cli/checks.js +41 -4
- package/node_modules/@pome-sh/twin-gmail/CHANGELOG.md +39 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.d.ts +7 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js +138 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.d.ts +3 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js +9 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.d.ts +4 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js +56 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.d.ts +14 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js +207 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.d.ts +10 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js +96 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.d.ts +113 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js +145 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.d.ts +2 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js +103 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.d.ts +17 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js +62 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.d.ts +19 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js +42 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.d.ts +2 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.js +4 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/package.json +1 -1
- 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/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.d.ts +3 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js +9 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js +123 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.d.ts +18 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js +264 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js +122 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.d.ts +83 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js +95 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.d.ts +9 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js +338 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.d.ts +24 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js +131 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.d.ts +22 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js +97 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/package.json +1 -1
- package/package.json +4 -4
- package/tasks/11-stripe-handle-failed-payment.md +2 -2
- package/tasks/12-stripe-reconcile-event.md +1 -1
- package/tasks/13-stripe-x402-payment-required.md +2 -2
- package/tasks/14-stripe-refund-retry.md +22 -9
- 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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { GmailDomain } from "./domain/index.js";
|
|
2
2
|
export type { DraftResource, FilterResource, LabelResource } from "./domain/index.js";
|
|
3
|
+
export { GMAIL_CHECKS } from "./checks.js";
|
|
4
|
+
export type { Check, GmailCheckState, GmailCheckStateDraft, GmailCheckStateLabel, GmailCheckStateMailbox, GmailCheckStateMessage, GmailCheckStateMessageLabel, } from "./checks.js";
|
|
3
5
|
export { openGmailTwinDatabase, migrate, resetDatabase } from "./db.js";
|
|
4
6
|
export { GmailError, gmailErrorEnvelope } from "./errors.js";
|
|
5
7
|
export { DEFAULT_GMAIL_EMAIL, identityFromSession, resolveUserEmail } from "./identity.js";
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
export { GmailDomain } from "./domain/index.js";
|
|
3
|
+
// F-1128 — the declared check vocabulary. Re-exported here on twin-slack's
|
|
4
|
+
// pattern; `@pome-sh/twin-gmail/checks` also resolves through the manifest's
|
|
5
|
+
// `"./*"` wildcard, which is the subpath pome-cloud and the CLI import.
|
|
6
|
+
export { GMAIL_CHECKS } from "./checks.js";
|
|
3
7
|
export { openGmailTwinDatabase, migrate, resetDatabase } from "./db.js";
|
|
4
8
|
export { GmailError, gmailErrorEnvelope } from "./errors.js";
|
|
5
9
|
export { DEFAULT_GMAIL_EMAIL, identityFromSession, resolveUserEmail } from "./identity.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,6BAA6B,EAC7B,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,6BAA6B,EAC7B,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -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":"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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"}
|