@volter/twin-github 0.1.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +88 -36
  2. package/client/github-mirror.css +277 -319
  3. package/client/github-mirror.d.ts +418 -0
  4. package/client/github-mirror.js +485 -0
  5. package/client/github-mirror.tsx +153 -357
  6. package/client/pulls-rest.ts +159 -0
  7. package/client/pulls-workspace.tsx +841 -0
  8. package/dist/client/github-mirror.bundle.js +239 -0
  9. package/dist/client/github-mirror.css +916 -0
  10. package/dist/client/github-mirror.d.ts +418 -0
  11. package/dist/client/github-mirror.js +485 -0
  12. package/dist/client/github-mirror.tsx +1315 -0
  13. package/dist/client/pulls-rest.d.ts +42 -0
  14. package/dist/client/pulls-rest.js +140 -0
  15. package/dist/client/pulls-rest.ts +159 -0
  16. package/dist/client/pulls-workspace.bundle.js +22 -0
  17. package/dist/client/pulls-workspace.d.ts +114 -0
  18. package/dist/client/pulls-workspace.js +418 -0
  19. package/dist/client/pulls-workspace.tsx +841 -0
  20. package/dist/src/cli.d.ts +2 -0
  21. package/dist/src/cli.js +40 -0
  22. package/dist/src/generated/graphql-sdl.gen.json +1 -0
  23. package/dist/src/generated/graphql.gen.json +1 -0
  24. package/dist/src/generated/surface.gen.json +1 -0
  25. package/dist/src/generated/ui.gen.json +1 -0
  26. package/dist/src/github-budget.d.ts +69 -0
  27. package/dist/src/github-budget.js +172 -0
  28. package/dist/src/github-capabilities.d.ts +5 -0
  29. package/dist/src/github-capabilities.js +4468 -0
  30. package/dist/src/github-conformance.d.ts +43 -0
  31. package/dist/src/github-conformance.js +76 -0
  32. package/dist/src/github-connector.d.ts +307 -0
  33. package/dist/src/github-connector.js +1398 -0
  34. package/dist/src/github-events.d.ts +41 -0
  35. package/dist/src/github-events.js +232 -0
  36. package/dist/src/github-git-http.d.ts +49 -0
  37. package/dist/src/github-git-http.js +185 -0
  38. package/dist/src/github-git-plane.d.ts +114 -0
  39. package/dist/src/github-git-plane.js +407 -0
  40. package/dist/src/github-mirror-state.d.ts +2 -0
  41. package/dist/src/github-mirror-state.js +335 -0
  42. package/dist/src/github-mirror-ui.d.ts +20 -0
  43. package/dist/src/github-mirror-ui.js +101 -0
  44. package/dist/src/github-server.d.ts +14 -0
  45. package/dist/src/github-server.js +240 -0
  46. package/dist/src/github-shared.d.ts +12 -0
  47. package/dist/src/github-shared.js +21 -0
  48. package/dist/src/github-twin.d.ts +1411 -0
  49. package/dist/src/github-twin.js +4084 -0
  50. package/dist/src/github-ui-conformance.d.ts +4 -0
  51. package/dist/src/github-ui-conformance.js +105 -0
  52. package/dist/src/github-ui-structure.d.ts +18 -0
  53. package/dist/src/github-ui-structure.js +251 -0
  54. package/dist/src/graphql-wire.d.ts +22 -0
  55. package/dist/src/graphql-wire.js +89 -0
  56. package/dist/src/index.d.ts +17 -0
  57. package/dist/src/index.js +104 -0
  58. package/dist/src/manifest.d.ts +8 -0
  59. package/dist/src/manifest.js +597 -0
  60. package/dist/src/npm-registry.d.ts +7 -0
  61. package/dist/src/npm-registry.js +47 -0
  62. package/dist/src/screens/app-installation.d.ts +3 -0
  63. package/dist/src/screens/app-installation.js +166 -0
  64. package/dist/src/screens/app-manifest.d.ts +3 -0
  65. package/dist/src/screens/app-manifest.js +81 -0
  66. package/dist/src/screens/oauth.d.ts +15 -0
  67. package/dist/src/screens/oauth.js +257 -0
  68. package/dist/src/screens/session.d.ts +8 -0
  69. package/dist/src/screens/session.js +170 -0
  70. package/dist/src/semantics/actions.d.ts +2 -0
  71. package/dist/src/semantics/actions.js +413 -0
  72. package/dist/src/semantics/activity.d.ts +4 -0
  73. package/dist/src/semantics/activity.js +161 -0
  74. package/dist/src/semantics/apps.d.ts +2 -0
  75. package/dist/src/semantics/apps.js +144 -0
  76. package/dist/src/semantics/branches.d.ts +2 -0
  77. package/dist/src/semantics/branches.js +136 -0
  78. package/dist/src/semantics/checks.d.ts +2 -0
  79. package/dist/src/semantics/checks.js +176 -0
  80. package/dist/src/semantics/code-scanning-upload.d.ts +2 -0
  81. package/dist/src/semantics/code-scanning-upload.js +97 -0
  82. package/dist/src/semantics/codespaces.d.ts +2 -0
  83. package/dist/src/semantics/codespaces.js +58 -0
  84. package/dist/src/semantics/commits.d.ts +2 -0
  85. package/dist/src/semantics/commits.js +109 -0
  86. package/dist/src/semantics/contents.d.ts +2 -0
  87. package/dist/src/semantics/contents.js +131 -0
  88. package/dist/src/semantics/deployments.d.ts +2 -0
  89. package/dist/src/semantics/deployments.js +127 -0
  90. package/dist/src/semantics/gists.d.ts +2 -0
  91. package/dist/src/semantics/gists.js +86 -0
  92. package/dist/src/semantics/git.d.ts +2 -0
  93. package/dist/src/semantics/git.js +235 -0
  94. package/dist/src/semantics/graphql.d.ts +7 -0
  95. package/dist/src/semantics/graphql.js +512 -0
  96. package/dist/src/semantics/index.d.ts +5 -0
  97. package/dist/src/semantics/index.js +62 -0
  98. package/dist/src/semantics/issues.d.ts +2 -0
  99. package/dist/src/semantics/issues.js +456 -0
  100. package/dist/src/semantics/keys.d.ts +2 -0
  101. package/dist/src/semantics/keys.js +66 -0
  102. package/dist/src/semantics/labels.d.ts +2 -0
  103. package/dist/src/semantics/labels.js +100 -0
  104. package/dist/src/semantics/meta.d.ts +12 -0
  105. package/dist/src/semantics/meta.js +144 -0
  106. package/dist/src/semantics/notifications.d.ts +4 -0
  107. package/dist/src/semantics/notifications.js +62 -0
  108. package/dist/src/semantics/orgs.d.ts +2 -0
  109. package/dist/src/semantics/orgs.js +420 -0
  110. package/dist/src/semantics/packages.d.ts +2 -0
  111. package/dist/src/semantics/packages.js +55 -0
  112. package/dist/src/semantics/pages.d.ts +2 -0
  113. package/dist/src/semantics/pages.js +176 -0
  114. package/dist/src/semantics/projects.d.ts +2 -0
  115. package/dist/src/semantics/projects.js +239 -0
  116. package/dist/src/semantics/pulls.d.ts +2 -0
  117. package/dist/src/semantics/pulls.js +462 -0
  118. package/dist/src/semantics/push-reactions.d.ts +101 -0
  119. package/dist/src/semantics/push-reactions.js +513 -0
  120. package/dist/src/semantics/releases.d.ts +19 -0
  121. package/dist/src/semantics/releases.js +231 -0
  122. package/dist/src/semantics/repo-invitations.d.ts +2 -0
  123. package/dist/src/semantics/repo-invitations.js +98 -0
  124. package/dist/src/semantics/repos.d.ts +17 -0
  125. package/dist/src/semantics/repos.js +390 -0
  126. package/dist/src/semantics/rulesets.d.ts +2 -0
  127. package/dist/src/semantics/rulesets.js +111 -0
  128. package/dist/src/semantics/search.d.ts +2 -0
  129. package/dist/src/semantics/search.js +84 -0
  130. package/dist/src/semantics/security.d.ts +2 -0
  131. package/dist/src/semantics/security.js +177 -0
  132. package/dist/src/semantics/shared.d.ts +61 -0
  133. package/dist/src/semantics/shared.js +151 -0
  134. package/dist/src/semantics/users.d.ts +2 -0
  135. package/dist/src/semantics/users.js +255 -0
  136. package/dist/src/semantics/webhooks.d.ts +2 -0
  137. package/dist/src/semantics/webhooks.js +107 -0
  138. package/dist/test-fixtures/github-a11y-reference.pr-list.SOURCE.md +56 -0
  139. package/dist/test-fixtures/github-a11y-reference.pr-list.json +1447 -0
  140. package/dist/test-fixtures/github-comment-schema.SOURCE.md +11 -0
  141. package/dist/test-fixtures/github-comment-schema.json +702 -0
  142. package/dist/test-fixtures/github-known-deviations.json +94 -0
  143. package/dist/test-fixtures/github-openapi-operations.SOURCE.md +76 -0
  144. package/dist/test-fixtures/github-openapi-operations.json +362 -0
  145. package/dist/test-fixtures/github-pull-schema.SOURCE.md +37 -0
  146. package/dist/test-fixtures/github-pull-schema.json +3601 -0
  147. package/dist/test-fixtures/github-review-schema.SOURCE.md +12 -0
  148. package/dist/test-fixtures/github-review-schema.json +236 -0
  149. package/package.json +20 -11
  150. package/src/cli.ts +7 -6
  151. package/src/generated/graphql-sdl.gen.json +1 -0
  152. package/src/generated/graphql.gen.json +1 -0
  153. package/src/generated/surface.gen.json +1 -0
  154. package/src/generated/ui.gen.json +1 -0
  155. package/src/github-a11y-snapshot.uitest.ts +5 -5
  156. package/src/github-budget.ts +4 -4
  157. package/src/github-capabilities.ts +1993 -556
  158. package/src/github-conformance.ts +12 -7
  159. package/src/github-connector.ts +108 -96
  160. package/src/github-events.ts +225 -95
  161. package/src/github-git-http.ts +48 -84
  162. package/src/github-git-plane.ts +247 -385
  163. package/src/github-journey.uitest.ts +28 -44
  164. package/src/github-mirror-state.ts +23 -61
  165. package/src/github-mirror-ui.ts +16 -10
  166. package/src/github-server.ts +158 -60
  167. package/src/github-shared.ts +1 -1
  168. package/src/github-twin.ts +1525 -4494
  169. package/src/github-ui-conformance.ts +7 -8
  170. package/src/github-ui-structure.ts +19 -18
  171. package/src/graphql-wire.ts +115 -0
  172. package/src/index.ts +50 -7
  173. package/src/manifest.ts +605 -0
  174. package/src/npm-registry.ts +43 -0
  175. package/src/screens/app-installation.tsx +217 -0
  176. package/src/screens/app-manifest.tsx +97 -0
  177. package/src/screens/oauth.tsx +258 -0
  178. package/src/screens/session.tsx +193 -0
  179. package/src/semantics/actions.ts +395 -0
  180. package/src/semantics/activity.ts +171 -0
  181. package/src/semantics/apps.ts +133 -0
  182. package/src/semantics/branches.ts +133 -0
  183. package/src/semantics/checks.ts +163 -0
  184. package/src/semantics/code-scanning-upload.ts +92 -0
  185. package/src/semantics/codespaces.ts +58 -0
  186. package/src/semantics/commits.ts +109 -0
  187. package/src/semantics/contents.ts +112 -0
  188. package/src/semantics/deployments.ts +116 -0
  189. package/src/semantics/gists.ts +85 -0
  190. package/src/semantics/git.ts +226 -0
  191. package/src/semantics/graphql.ts +505 -0
  192. package/src/semantics/index.ts +68 -0
  193. package/src/semantics/issues.ts +434 -0
  194. package/src/semantics/keys.ts +66 -0
  195. package/src/semantics/labels.ts +91 -0
  196. package/src/semantics/meta.ts +139 -0
  197. package/src/semantics/notifications.ts +58 -0
  198. package/src/semantics/orgs.ts +383 -0
  199. package/src/semantics/packages.ts +59 -0
  200. package/src/semantics/pages.ts +154 -0
  201. package/src/semantics/projects.ts +246 -0
  202. package/src/semantics/pulls.ts +421 -0
  203. package/src/semantics/push-reactions.ts +471 -0
  204. package/src/semantics/releases.ts +213 -0
  205. package/src/semantics/repo-invitations.ts +112 -0
  206. package/src/semantics/repos.ts +373 -0
  207. package/src/semantics/rulesets.ts +107 -0
  208. package/src/semantics/search.ts +83 -0
  209. package/src/semantics/security.ts +153 -0
  210. package/src/semantics/shared.ts +181 -0
  211. package/src/semantics/users.ts +252 -0
  212. package/src/semantics/webhooks.ts +101 -0
  213. package/test-fixtures/github-known-deviations.json +7 -8
  214. package/test-fixtures/github-openapi-operations.json +46 -227
  215. package/src/github-graphql.ts +0 -398
@@ -0,0 +1,43 @@
1
+ import { specConformance } from '@volter/world-tooling';
2
+ type JsonSchema = specConformance.JsonSchema;
3
+ type KnownDeviation = specConformance.KnownDeviation;
4
+ type SpecViolation = specConformance.SpecViolation;
5
+ export type GithubViolation = SpecViolation & {
6
+ prId: string;
7
+ };
8
+ export type GithubConformanceReport = {
9
+ ok: boolean;
10
+ prsChecked: number;
11
+ fieldsChecked: number;
12
+ violations: GithubViolation[];
13
+ knownIgnored: number;
14
+ };
15
+ /** Load the vendored full GitHub pull-request JSON Schema (dereferenced OpenAPI). */
16
+ export declare function loadGithubPrSchema(): Promise<JsonSchema>;
17
+ /** Load the vendored GitHub pull-request-review JSON Schema. */
18
+ export declare function loadGithubReviewSchema(): Promise<JsonSchema>;
19
+ /** Load the vendored GitHub issue-comment JSON Schema. */
20
+ export declare function loadGithubCommentSchema(): Promise<JsonSchema>;
21
+ /** Load the declared evidence-twin scope (fields deliberately not modeled, + reasons). */
22
+ export declare function loadGithubKnownDeviations(): Promise<KnownDeviation[]>;
23
+ /**
24
+ * Validate every PR the twin serves against the real GitHub PR schema. A wrong
25
+ * type, an undeclared missing-required field, a fabricated field, or an enum
26
+ * violation fails the report.
27
+ */
28
+ export declare function checkGithubConformance(schema: JsonSchema, opts?: {
29
+ root?: string;
30
+ known?: KnownDeviation[];
31
+ }): GithubConformanceReport;
32
+ export type GithubCoverageReport = specConformance.SpecCoverageReport;
33
+ /**
34
+ * Top-level coverage: of the fields the GitHub PR schema declares, which does the
35
+ * twin emit? The "what we emulate / what we're missing" report — computed from the
36
+ * spec, not probed live. Uses a representative content-bearing PR sample.
37
+ */
38
+ export declare function githubCoverage(schema: JsonSchema): GithubCoverageReport;
39
+ /** Coverage of the pull-request-review object the twin emits on review submission. */
40
+ export declare function githubReviewCoverage(schema: JsonSchema): GithubCoverageReport;
41
+ /** Coverage of the issue-comment object the twin emits on comment creation. */
42
+ export declare function githubCommentCoverage(schema: JsonSchema): GithubCoverageReport;
43
+ export {};
@@ -0,0 +1,76 @@
1
+ // GitHub twin conformance (scorecard R2) — spec-conformance against GitHub's
2
+ // PUBLISHED pull-request schema, not a hand-authored field-name list. Offline, no
3
+ // token: for every PR the twin serves, validate the REST response against the real
4
+ // schema (types, required, enums, fabricated extras) vendored from GitHub's OpenAPI
5
+ // (test-fixtures/github-pull-schema.json). Twin-namespaced extras (keys starting
6
+ // `_`) are skipped. Fields the evidence twin deliberately does not model are DECLARED
7
+ // in github-known-deviations.json with reasons — a missing-required field that is
8
+ // NOT declared fails CI. The companion coverage report says what we emit vs what the
9
+ // vendor declares. Standing gate: wrong types, undeclared omissions, fabricated
10
+ // surface, or enum violations all fail.
11
+ import { readFile } from 'node:fs/promises';
12
+ import { specConformance } from '@volter/world-tooling';
13
+ import { githubState, toGithubComment, toGithubRest, toGithubReview } from "./github-twin.js";
14
+ const isTwinExtra = (f) => f.startsWith('_');
15
+ const fixturePath = (name) => new URL(`../test-fixtures/${name}`, import.meta.url).pathname;
16
+ /** Load the vendored full GitHub pull-request JSON Schema (dereferenced OpenAPI). */
17
+ export async function loadGithubPrSchema() {
18
+ return JSON.parse(await readFile(fixturePath('github-pull-schema.json'), 'utf8'));
19
+ }
20
+ /** Load the vendored GitHub pull-request-review JSON Schema. */
21
+ export async function loadGithubReviewSchema() {
22
+ return JSON.parse(await readFile(fixturePath('github-review-schema.json'), 'utf8'));
23
+ }
24
+ /** Load the vendored GitHub issue-comment JSON Schema. */
25
+ export async function loadGithubCommentSchema() {
26
+ return JSON.parse(await readFile(fixturePath('github-comment-schema.json'), 'utf8'));
27
+ }
28
+ /** Load the declared evidence-twin scope (fields deliberately not modeled, + reasons). */
29
+ export async function loadGithubKnownDeviations() {
30
+ const doc = JSON.parse(await readFile(fixturePath('github-known-deviations.json'), 'utf8'));
31
+ return doc.deviations;
32
+ }
33
+ /**
34
+ * Validate every PR the twin serves against the real GitHub PR schema. A wrong
35
+ * type, an undeclared missing-required field, a fabricated field, or an enum
36
+ * violation fails the report.
37
+ */
38
+ export function checkGithubConformance(schema, opts = {}) {
39
+ const state = githubState(opts.root);
40
+ const violations = [];
41
+ let fieldsChecked = 0;
42
+ let knownIgnored = 0;
43
+ // "Every PR the twin SERVES" — which is the rows marked as pull-request evidence, not the
44
+ // counter shells `ensure()` materializes for issue numbers. Checking a shell measures an
45
+ // object no door answers with, and its emptiness would read as the twin's conformance.
46
+ const served = state.prs.filter((p) => p.is_pull_request);
47
+ for (const pr of served) {
48
+ const rest = toGithubRest(pr);
49
+ const rep = specConformance.checkSpecConformance(rest, schema, { exemptKey: isTwinExtra, known: opts.known ?? [] });
50
+ fieldsChecked += rep.fieldsChecked;
51
+ knownIgnored += rep.knownIgnored;
52
+ for (const v of rep.violations)
53
+ violations.push({ ...v, prId: pr.id });
54
+ }
55
+ return { ok: violations.length === 0, prsChecked: served.length, fieldsChecked, violations, knownIgnored };
56
+ }
57
+ /**
58
+ * Top-level coverage: of the fields the GitHub PR schema declares, which does the
59
+ * twin emit? The "what we emulate / what we're missing" report — computed from the
60
+ * spec, not probed live. Uses a representative content-bearing PR sample.
61
+ */
62
+ export function githubCoverage(schema) {
63
+ const sample = toGithubRest({
64
+ id: 'o/r#1', number: 1, repository: 'o/r', base_ref: 'main', head_sha: 'abc123',
65
+ changed_files: 3, commits: 2, review_count: 0, comment_count: 0, title: 'Sample', state: 'open',
66
+ });
67
+ return specConformance.specCoverage(schema, sample, { exemptKey: isTwinExtra });
68
+ }
69
+ /** Coverage of the pull-request-review object the twin emits on review submission. */
70
+ export function githubReviewCoverage(schema) {
71
+ return specConformance.specCoverage(schema, toGithubReview({ id: 1, repository: 'o/r', number: 1, state: 'APPROVED', body: 'lgtm' }), { exemptKey: isTwinExtra });
72
+ }
73
+ /** Coverage of the issue-comment object the twin emits on comment creation. */
74
+ export function githubCommentCoverage(schema) {
75
+ return specConformance.specCoverage(schema, toGithubComment({ id: 1, repository: 'o/r', number: 1, body: 'hi', at: '2026-01-01T00:00:00Z' }), { exemptKey: isTwinExtra });
76
+ }
@@ -0,0 +1,307 @@
1
+ import type { PerformContext, PushOutcome, RemoteExecute, SyncResource, TwinAction } from '@volter/world-core';
2
+ import { GithubBudget, type GithubBudgetOptions } from './github-budget.js';
3
+ /**
4
+ * The minimal real-GitHub REST surface the connector needs. A real `@octokit/rest`
5
+ * client is structurally adaptable to this (its `request(route, params)` returns
6
+ * `{ status, data }`) — the consumer wires it; this pack never imports it. `request`
7
+ * is the single credentialed boundary: in tests it's a fake, live it's the user's
8
+ * token-bound octokit. It maps a REST `route` ("METHOD /path") + params to a result.
9
+ */
10
+ export interface GithubExecute {
11
+ request(route: string, params?: Record<string, unknown>): Promise<{
12
+ status: number;
13
+ data: any;
14
+ }>;
15
+ }
16
+ /** Construction options for the live executor. `budget` cannot be null and cannot be loosened. */
17
+ export type LiveGithubOptions = {
18
+ /** Injected `fetch`, so a test can COUNT the requests the guard did or did not let through. */
19
+ fetchImpl?: typeof fetch;
20
+ /** An existing budget to share across executors. Omit and one is constructed. Cannot be null. */
21
+ budget?: GithubBudget;
22
+ /** Construction options for the default budget (ledger path, clock). Cannot loosen it. */
23
+ budgetOptions?: GithubBudgetOptions;
24
+ };
25
+ /**
26
+ * A live executor against the real GitHub REST API (token = the user's own PAT).
27
+ * Constructed with the real @octokit/rest in PROD by the CALLER and passed in; this
28
+ * helper shows the shape without importing the SDK. Kept tiny + dependency-free: it
29
+ * uses `fetch`, so the pack pulls in no network client. Live runs may instead pass a
30
+ * real `new Octokit({ auth }).request` bound into a `{ request }` object.
31
+ *
32
+ * THIS IS THE ONE PLACE this pack issues a live `api.github.com` request, and therefore the one
33
+ * place the rate budget has to be enforced. EVERY call is guarded: the budget is charged BEFORE the
34
+ * request goes out (`checkBudget`, which THROWS `GithubBudgetError` instead of returning when the
35
+ * ceiling or a cooldown says stop) and the response is fed back (`recordCall`) so a `Retry-After` /
36
+ * 403-or-429 / `x-ratelimit-remaining: 0` signal becomes a persisted cooldown that makes every
37
+ * later call fail fast WITHOUT touching GitHub. The weights ARE GitHub's own published point costs
38
+ * (1 for a read, 5 for a write) — see `github-budget.ts`. There is deliberately NO option to
39
+ * disable the guard, and no value a caller can pass for `budget` that yields an unguarded client —
40
+ * but NOT immunity from a caller who WANTS one (a fresh `budgetOptions.path` or an injected clock
41
+ * restores the allowance; the kernel header states that limit and this does not upgrade it).
42
+ */
43
+ export declare function liveGithubExecute(token: string, baseUrl?: string, opts?: LiveGithubOptions): GithubExecute;
44
+ type ObservedReviewComment = {
45
+ id: string;
46
+ /** WHO wrote THIS finding, from the comment's own `user` — the row the vendor sent, not
47
+ * the review that wraps it. A review is a join key, not an authorship claim: a scanner
48
+ * App's finding rides a review a human submitted, a threaded reply rides the root's
49
+ * review, and the phantom bucket has no author at all. The pull BOUGHT this field and
50
+ * used to throw it away, so a bot finding was served under the human reviewer's login —
51
+ * the exact fact a consumer triaging findings reads. Absent when the vendor named nobody
52
+ * (`user: null`, a deleted account); the wrapper's author is then the only evidence
53
+ * there is, and the fold falls back to it rather than inventing one. */
54
+ authorLogin?: string;
55
+ authorType?: 'bot' | 'user';
56
+ path?: string;
57
+ line?: number;
58
+ body?: string;
59
+ inReplyTo?: string;
60
+ createdAt?: string;
61
+ };
62
+ type ObservedReview = {
63
+ id?: string;
64
+ /** True for a review NOBODY SHOWED US — one the reviews page never returned. Two shapes
65
+ * wear it: the PHANTOM bucket (`unattached`), holding inline comments whose review id was
66
+ * missing, and a NAMED orphan, whose id an inline comment gave us but whose row never
67
+ * arrived (a review deleted between the two reads, a page boundary). Both carry comments
68
+ * and NOTHING ELSE — no state, no author, no verdict — and a consumer must be able to tell
69
+ * either from a real review it merely has no state for yet, so both say so. */
70
+ partial?: boolean;
71
+ authorLogin?: string;
72
+ authorType?: 'bot' | 'user';
73
+ state?: string;
74
+ body?: string;
75
+ submittedAt?: string;
76
+ commitId?: string;
77
+ comments: ObservedReviewComment[];
78
+ };
79
+ type ObservedComment = {
80
+ id?: string;
81
+ authorLogin?: string;
82
+ authorType?: 'bot' | 'user';
83
+ body?: string;
84
+ createdAt?: string;
85
+ updatedAt?: string;
86
+ };
87
+ type ObservedPr = {
88
+ id: string;
89
+ number: number;
90
+ repository: string;
91
+ title?: string;
92
+ body?: string;
93
+ state?: string;
94
+ draft?: boolean;
95
+ merged?: boolean;
96
+ mergeCommit?: string;
97
+ baseRef?: string;
98
+ headSha?: string;
99
+ changedFiles?: number;
100
+ commitsCount?: number;
101
+ authorLogin?: string | null;
102
+ authorType?: string;
103
+ /** The PR's own `updated_at` — the provider instant that dates its delta AND the stamp
104
+ * the next pull compares against to decide whether its conversation is worth buying. */
105
+ updatedAt?: string;
106
+ /** False when the budget skipped this PR's reviews/comments (its `updated_at` had not
107
+ * moved). The counts and the conversation subjects are then OMITTED rather than
108
+ * reported as zero — an unbought fact is not an observation of absence. */
109
+ detailsFetched: boolean;
110
+ /** How many reviews this PR has — counted over exactly the set `latestReview` is drawn
111
+ * from (submitted, non-PENDING), so the count and the newest verdict can never disagree. */
112
+ reviewCount?: number;
113
+ /** How many issue comments the conversation read bought. Emitted onto the subject beside
114
+ * `reviewCount` (it used to be computed here and dropped on the floor). */
115
+ commentCount?: number;
116
+ reviews?: ObservedReview[];
117
+ comments?: ObservedComment[];
118
+ };
119
+ type ObservedIssue = {
120
+ id: string;
121
+ number: number;
122
+ repository: string;
123
+ title?: string;
124
+ body?: string;
125
+ state?: string;
126
+ created_at?: string;
127
+ updated_at?: string;
128
+ };
129
+ export declare function pullGithubPrs(execute: GithubExecute, opts: {
130
+ owner: string;
131
+ repo: string;
132
+ state?: 'open' | 'closed' | 'all';
133
+ perPage?: number;
134
+ /** How many recently-updated CLOSED PRs an open-only pull also sweeps (default 20). */
135
+ closedPerPage?: number;
136
+ /** `owner/repo#n` → the `updated_at` last observed for it. */
137
+ lastUpdatedAt?: Record<string, string>;
138
+ }): Promise<ObservedPr[]>;
139
+ /**
140
+ * Pull OBSERVED ISSUES for a repo via the injected executor (GET /repos/:o/:r/issues),
141
+ * EXCLUDING pull requests — the issues endpoint returns PRs too (each PR is an issue),
142
+ * distinguished by a `pull_request` field, which we filter out so PRs only flow through
143
+ * pullGithubPrs. Folds each issue's content (title/body/state). Numbers share the
144
+ * per-repo PR/issue space (real GitHub).
145
+ */
146
+ export declare function pullGithubIssues(execute: GithubExecute, opts: {
147
+ owner: string;
148
+ repo: string;
149
+ state?: 'open' | 'closed' | 'all';
150
+ perPage?: number;
151
+ }): Promise<ObservedIssue[]>;
152
+ export declare function observedPrsToResources(prs: ObservedPr[]): SyncResource[];
153
+ /**
154
+ * The `updated_at` this world last observed per PR — the budget's memory, read from the
155
+ * SHADOW (the fold of the world's own event log) rather than a cache beside it, so it
156
+ * survives a restart, is per-world like every other pulled fact, and cannot disagree with
157
+ * what was actually folded. A PR nobody has pulled yet is simply absent, and its
158
+ * conversation gets bought.
159
+ */
160
+ export declare function lastObservedPrUpdates(root?: string): Record<string, string>;
161
+ export declare function observedIssuesToResources(issues: ObservedIssue[]): SyncResource[];
162
+ /**
163
+ * A push this connector DECLINES to make, as opposed to one the vendor rejected. It is
164
+ * raised when enacting the local write faithfully is impossible — the only case today is a
165
+ * threaded reply whose root has no id on the real repo — and the alternative (posting it
166
+ * somewhere else, or forwarding a twin-minted id) would write the wrong thing to a real
167
+ * account. A refusal is ledgered and the sweep continues; the action stays PENDING, so it
168
+ * pushes on a later sweep once the root is known.
169
+ */
170
+ export declare class GithubPushRefused extends Error {
171
+ constructor(message: string);
172
+ }
173
+ /**
174
+ * PULL + FOLD: pull a repo's OBSERVED PRs AND issues and fold them into the twin (mirror
175
+ * seeding). Folds CONTENT (PR/issue title/body/state, review state/body, comment body)
176
+ * plus metadata (counts/refs + review/comment existence) — only the Non-goal (repo file
177
+ * CONTENTS) is excluded. Also threads the same resources through `syncPull` so the generic
178
+ * shadow-diff dedup path is exercised (the Linear/Slack-shared contract); the github fold
179
+ * carries the counts/content the generic delta path drops. PRs and issues share ONE
180
+ * per-repo number space (real GitHub). Re-pulling identical observations appends nothing.
181
+ *
182
+ * Per-row conflict tolerance: if a stored event has DIVERGED from what a resource now
183
+ * folds to (a `Conflicting duplicate` — e.g. a post-append line mutation raced the live
184
+ * writer), that ONE row is skipped and its stable id recorded, rather than aborting the
185
+ * whole repo's fold — one row's integrity question must not become a total observation
186
+ * outage for the ~dozens of other resources in the same pull (peak-internal PH-216). The
187
+ * result exposes `conflictsSkipped` + `conflictingIds` so a poller can log the integrity
188
+ * problem loudly instead of it being swallowed. Every OTHER append error stays fatal.
189
+ */
190
+ export declare function syncGithubFromReal(execute: GithubExecute, opts: {
191
+ owner: string;
192
+ repo: string;
193
+ root?: string;
194
+ occurredAt: string;
195
+ state?: 'open' | 'closed' | 'all';
196
+ perPage?: number;
197
+ }): Promise<{
198
+ observed: number;
199
+ deltasAppended: number;
200
+ eventsAppended: number;
201
+ issues: number;
202
+ conflictsSkipped: number;
203
+ conflictingIds: string[];
204
+ }>;
205
+ /**
206
+ * Push ONE pending GitHub action to the real vendor via the injected executor. Maps
207
+ * the twin's local write (recorded by applyGithubWrite as an action with an
208
+ * `operation` + `fields`) to the matching REST call. The injected `execute.request`
209
+ * is the SOLE credentialed boundary. Returns the real external id when the API gives
210
+ * one (PR/issue/milestone number, review/comment/status/check id, merge sha). Throws
211
+ * on a non-2xx so a failure is never silent.
212
+ *
213
+ * Covers EVERY write operation applyGithubWrite emits, each faithfully mapped to its
214
+ * GitHub REST call (method/path/body):
215
+ * pull_request.create POST .../pulls
216
+ * pull_request.update PATCH .../pulls/:n (+ PATCH .../issues/:n
217
+ * for label/assignee/milestone fields)
218
+ * pull_request.merge PUT .../pulls/:n/merge
219
+ * pull_request.request_reviewers POST .../pulls/:n/requested_reviewers
220
+ * pull_request.remove_requested_reviewers DELETE .../pulls/:n/requested_reviewers
221
+ * pull_request_review.submit POST .../pulls/:n/reviews
222
+ * pull_request_review_comment.create POST .../pulls/:n/comments
223
+ * issue.create POST .../issues
224
+ * issue.update PATCH .../issues/:n
225
+ * issue_comment.create POST .../issues/:n/comments
226
+ * commit_status.create POST .../statuses/:sha
227
+ * check_run.create POST .../check-runs
228
+ * milestone.create POST .../milestones
229
+ * milestone.update PATCH .../milestones/:n
230
+ * Pushing sends the LOCAL content (titles/bodies/diffs the fork authored) — that is
231
+ * legitimate (only PULL must not fabricate content). Unknown ops FAIL LOUDLY (throw).
232
+ */
233
+ export declare function pushGithubAction(execute: GithubExecute, action: {
234
+ operation?: string;
235
+ subject: {
236
+ type: string;
237
+ id: string;
238
+ };
239
+ fields?: Record<string, unknown>;
240
+ }, opts?: {
241
+ /** Twin comment id → the id that row has on the REAL repo (pulled from GitHub, or
242
+ * returned by an earlier push in this sweep). A reply resolves its root here. */
243
+ externalIds?: Record<string, string>;
244
+ }): Promise<{
245
+ externalId: string;
246
+ }>;
247
+ /**
248
+ * THE R14 PUSH ADAPTER for github (jira's `pushJiraToRemote` is the reference; this
249
+ * transcribes its METHOD): pending local actions cross to the remote through the kernel's
250
+ * ONE RemoteExecute seam under a sealed credential the pack never sees, and each pushed
251
+ * action is confirmed in the local log. Anchored naming: `createGithubTwinFetch` pairs
252
+ * with `syncGithubFromRemote` and `pushGithubToRemote`. Push-plane only — a read refuses
253
+ * loudly. The route grammar is the one `pushGithubAction` speaks (`METHOD /path/{param}`,
254
+ * templated params in the path, the rest as the JSON body).
255
+ */
256
+ /** This pack's vendor executor over the kernel's ONE RemoteExecute (push plane). */
257
+ export declare function githubPushExecutor(execute: RemoteExecute): GithubExecute;
258
+ /** THE ANCHORED PUSH SEAM (contract "The push arm is the kernel's transaction; a pack performs one
259
+ * action"): perform ONE pending action against GitHub over the kernel executor; the vendor's id comes back. */
260
+ /** The vendor's id in the pack's own subject grammar: a number replaces the trailing number of the
261
+ * local id (`acme/web#issue:1` → `acme/web#issue:57`, `acme/web#3` → `acme/web#9`, `comment:2` →
262
+ * `comment:184`); anything else (a sha, a ref, a path, an owner/name) leaves the address as it is
263
+ * and rides on the receipt as `vendorId`. The kernel rebinds the subject to what comes back
264
+ * (contract "A pushed write adopts the vendor's id"). */
265
+ export declare function adoptGithubId(localId: string, vendorId: string | undefined): string;
266
+ export declare function performGithubAction(execute: RemoteExecute, action: TwinAction, ctx: PerformContext): Promise<PushOutcome>;
267
+ /**
268
+ * THE REPOSITORY AND ITS BRANCHES are observed with the PRs and issues: a working copy's
269
+ * confirmed writes (a branch it cut, the repo it declared) survive only as what reality
270
+ * shows back, so the pull says what reality holds — the repo and every branch head.
271
+ */
272
+ export declare function observeRepositoryAndBranches(execute: GithubExecute, owner: string, repo: string): Promise<SyncResource[]>;
273
+ /**
274
+ * THE R14 SCHEDULED-PULL ADAPTER (jira is the reference; this transcribes its METHOD):
275
+ * adapts this pack's executor onto the kernel's ONE RemoteExecute seam, so the twins
276
+ * service can schedule pulls with a sealed credential the pack never sees. Anchored
277
+ * naming: `createGithubTwinFetch` pairs with `syncGithubFromRemote`.
278
+ *
279
+ * The link's origin names the REPO, not just the API host — a link is a git remote:
280
+ * https://api.github.com/repos/{owner}/{repo}
281
+ * Egress still anchors at the origin's HOST (the service's RemoteExecute discards the
282
+ * path when routing), so the path here is pure identity. Pull-plane only — every
283
+ * non-read request refuses loudly.
284
+ *
285
+ * Rate note, counted over the routes this pull actually calls: 2 list reads (the repo and
286
+ * its branches) + 1 PR list + 1 issue list, and then, per PR whose `updated_at` MOVED since
287
+ * the last pull, three PAGED conversation reads — the reviews, the issue comments and the
288
+ * inline comments — at 1 request each for a PR under 100 rows and up to 10 each beyond
289
+ * that. So a poll costs 4 + 3·CHANGED at the floor and 4 + 30·CHANGED at the ceiling; an
290
+ * unchanged PR costs nothing beyond the list. The link's sync interval still carries the
291
+ * budget — 60s at the default page floods a PAT's 5000/hr the first time it walks a busy
292
+ * repo; schedule github links at ≥120s.
293
+ */
294
+ export declare function syncGithubFromRemote(execute: RemoteExecute, opts?: {
295
+ root?: string;
296
+ origin?: string;
297
+ state?: 'open' | 'closed' | 'all';
298
+ perPage?: number;
299
+ }): Promise<{
300
+ observed: number;
301
+ deltasAppended: number;
302
+ eventsAppended: number;
303
+ issues: number;
304
+ conflictsSkipped: number;
305
+ conflictingIds: string[];
306
+ }>;
307
+ export {};