@clemrig/factory 0.5.1 → 0.6.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.
@@ -1,3 +1,5 @@
1
+ import { rm, rmdir } from "node:fs/promises";
2
+ import { basename, dirname, join } from "node:path";
1
3
  import { runProcess } from "@clemrig/flow";
2
4
  /** Runs git in the repository; a failure carries git's own message. */
3
5
  async function git(dir, args, timeoutMs = 60_000) {
@@ -9,6 +11,20 @@ async function git(dir, args, timeoutMs = 60_000) {
9
11
  async function output(dir, args) {
10
12
  return (await git(dir, args)).stdout.trim();
11
13
  }
14
+ /**
15
+ * Git's output when the run read all of it: a list cut by the capture limit
16
+ * would name wrong paths, so the run stops instead.
17
+ */
18
+ async function whole(dir, args) {
19
+ const result = await git(dir, args);
20
+ if (result.stdoutTruncated)
21
+ throw new Error(`git ${args[0]} wrote more than the run reads (2 MiB): the run stops`);
22
+ return result.stdout;
23
+ }
24
+ /** NUL-separated fields of Git's `-z` output. */
25
+ async function fields(dir, args) {
26
+ return (await whole(dir, args)).split("\0").filter(Boolean);
27
+ }
12
28
  /** A branch's full reference: a tag of the same name never answers for it. */
13
29
  export function branchRef(branch) {
14
30
  return `refs/heads/${branch}`;
@@ -47,6 +63,11 @@ export async function hasChanges(dir, path, except) {
47
63
  export async function fetchBranch(dir, branch) {
48
64
  await git(dir, ["fetch", "origin", `+${branchRef(branch)}:${remoteRef(branch)}`], 120_000);
49
65
  }
66
+ /** Whether Git still has this object. */
67
+ export async function hasObject(dir, id) {
68
+ return ((await runProcess(["git", "cat-file", "-e", id], dir, 60_000)).exitCode ===
69
+ 0);
70
+ }
50
71
  export function commitOf(dir, ref) {
51
72
  return output(dir, ["rev-parse", ref]);
52
73
  }
@@ -60,14 +81,157 @@ export async function commit(dir, message, paths) {
60
81
  await git(dir, ["commit", "-m", message, ...(paths ? ["--", ...paths] : [])]);
61
82
  return commitOf(dir, "HEAD");
62
83
  }
63
- /** Every change since a commit, new files included: stages them all, then diffs the index. */
64
- export async function stagedDiff(dir, since) {
65
- await git(dir, ["add", "-A"]);
66
- const result = await git(dir, ["diff", "--cached", since]);
67
- return { text: result.stdout, truncated: result.stdoutTruncated };
68
- }
69
84
  export async function forcePush(dir, branch) {
70
85
  const ref = branchRef(branch);
71
86
  await git(dir, ["push", "--force", "-u", "origin", `${ref}:${ref}`], 120_000);
72
87
  }
88
+ /** The diff from one commit or tree to another. */
89
+ export async function diff(dir, before, after) {
90
+ const result = await git(dir, ["diff", before, after]);
91
+ return { text: result.stdout, truncated: result.stdoutTruncated };
92
+ }
93
+ /** Each file that differs from one commit or tree to another, with its added and removed lines, renames seen as `diff` sees them. */
94
+ export async function diffStat(dir, before, after) {
95
+ const records = (await fields(dir, ["diff", "--numstat", "-z", before, after])).values();
96
+ const files = [];
97
+ for (const record of records) {
98
+ const [added = "-", removed = "-", ...rest] = record.split("\t");
99
+ let path = rest.join("\t");
100
+ let from;
101
+ // A rename leaves the path empty, then gives its source and destination.
102
+ if (path === "") {
103
+ from = records.next().value ?? "";
104
+ path = records.next().value ?? "";
105
+ }
106
+ files.push({
107
+ path,
108
+ ...(from === undefined ? {} : { from }),
109
+ ...(added === "-" ? {} : { added: Number(added) }),
110
+ ...(removed === "-" ? {} : { removed: Number(removed) }),
111
+ });
112
+ }
113
+ return files;
114
+ }
115
+ /** The files changed since a commit, untracked ones included, without staging anything. */
116
+ export async function changedFiles(dir, since) {
117
+ const tracked = await fields(dir, [
118
+ "diff",
119
+ "--name-only",
120
+ "-z",
121
+ "--no-renames",
122
+ since,
123
+ ]);
124
+ const untracked = await fields(dir, [
125
+ "ls-files",
126
+ "--others",
127
+ "--exclude-standard",
128
+ "-z",
129
+ ]);
130
+ return [...new Set([...tracked, ...untracked])];
131
+ }
132
+ /**
133
+ * The working tree as a tree object: tracked and untracked files, ignored ones
134
+ * aside. It stages every change, as a delivery does.
135
+ */
136
+ export async function snapshot(dir) {
137
+ await git(dir, ["add", "-A"]);
138
+ return output(dir, ["write-tree"]);
139
+ }
140
+ /** The files that differ from `before` to `after`, renames seen as a deletion and an addition. */
141
+ export async function changesBetween(dir, before, after) {
142
+ const records = (await fields(dir, [
143
+ "diff-tree",
144
+ "-r",
145
+ "-z",
146
+ "--no-renames",
147
+ "--name-status",
148
+ before,
149
+ after,
150
+ ])).values();
151
+ const changes = [];
152
+ // Each status is followed by its path.
153
+ for (const code of records)
154
+ changes.push({
155
+ status: code === "A" ? "added" : code === "D" ? "deleted" : "modified",
156
+ path: records.next().value ?? "",
157
+ });
158
+ return changes;
159
+ }
160
+ /**
161
+ * Puts back the files that changed since the snapshot `before`, in the index
162
+ * and the working tree. The added ones go first, with the folders they leave
163
+ * empty: a folder that replaced a file or a link is gone before the file or
164
+ * the link comes back, and nothing is ever removed through a link put back.
165
+ * Then the modified and deleted ones are rewritten. Ignored files stay.
166
+ */
167
+ export async function restore(dir, before, changes) {
168
+ const added = changes.filter(({ status }) => status === "added");
169
+ const rewritten = changes.filter(({ status }) => status !== "added");
170
+ await git(dir, ["read-tree", before]);
171
+ for (const { path } of added) {
172
+ await rm(join(dir, path), { force: true });
173
+ await removeEmptyParents(dir, path);
174
+ }
175
+ if (rewritten.length > 0)
176
+ await git(dir, [
177
+ "checkout-index",
178
+ "-f",
179
+ "--",
180
+ ...rewritten.map(({ path }) => path),
181
+ ]);
182
+ }
183
+ /** Removes the folders of `path` that are now empty, from the deepest up to the repository. */
184
+ async function removeEmptyParents(dir, path) {
185
+ for (let parent = dirname(path); parent !== "."; parent = dirname(parent)) {
186
+ try {
187
+ await rmdir(join(dir, parent));
188
+ }
189
+ catch {
190
+ // Not empty, or already gone: the folders above it stay too.
191
+ return;
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Puts back everything that changed in the working tree since the snapshot
197
+ * `before`; returns the paths it put back, none when nothing changed. A
198
+ * snapshot depends on the ignore rules, so they come back first, each time
199
+ * followed by a new snapshot (`nextToPutBack`): the other changes are then
200
+ * what they really are, and an ignored file is never taken for one.
201
+ */
202
+ export async function putBack(dir, before) {
203
+ const put = new Set();
204
+ let after = await snapshot(dir);
205
+ for (let round = 0; after !== before; round += 1) {
206
+ // Past this many rounds, something else keeps rewriting the ignore rules.
207
+ if (round === 20)
208
+ throw new Error("The working tree's .gitignore files keep changing: the run stops");
209
+ const next = nextToPutBack(await changesBetween(dir, before, after));
210
+ await restore(dir, before, next);
211
+ for (const { path } of next)
212
+ put.add(path);
213
+ after = await snapshot(dir);
214
+ }
215
+ return [...put].sort();
216
+ }
217
+ /**
218
+ * What `putBack` puts back next: the `.gitignore` files that were there, else
219
+ * the shallowest new ones, else every change. A rule only exposes files in its
220
+ * folder and below, so a deeper `.gitignore` that seems new may be an ignored
221
+ * file it exposed: it waits for the next snapshot.
222
+ */
223
+ function nextToPutBack(changes) {
224
+ const ignores = changes.filter(({ path }) => basename(path) === ".gitignore");
225
+ if (ignores.length === 0)
226
+ return changes;
227
+ const existing = ignores.filter(({ status }) => status !== "added");
228
+ if (existing.length > 0)
229
+ return existing;
230
+ const shallowest = Math.min(...ignores.map(({ path }) => depth(path)));
231
+ return ignores.filter(({ path }) => depth(path) === shallowest);
232
+ }
233
+ /** How deep a path is: 1 for a file at the root of the repository. */
234
+ function depth(path) {
235
+ return path.split("/").length;
236
+ }
73
237
  //# sourceMappingURL=git.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/system/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/system/git.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- export type ArtifactKind = "issue" | "spec" | "plan";
1
+ /** An issue's files: a folder with none of them is not an issue. */
2
+ declare const artifactKinds: readonly ["issue", "spec", "plan"];
3
+ export type ArtifactKind = (typeof artifactKinds)[number];
2
4
  /** Where the issues are, relative to the repository. */
3
5
  export declare const issuesPath: string;
4
6
  /** `.factory/issues/<slug>/<kind>.md`, relative to the repository: the only place that names these paths. */
@@ -18,7 +20,10 @@ export interface IssueFolder {
18
20
  export declare function listIssueFolders(repository: string): Promise<IssueFolder[]>;
19
21
  export declare function writeArtifact(repository: string, slug: string, kind: ArtifactKind, text: string): Promise<string>;
20
22
  export declare function hasArtifact(repository: string, slug: string, kind: ArtifactKind): Promise<boolean>;
23
+ /** Whether `slug` names an issue: a folder with one of its files. */
24
+ export declare function isIssue(repository: string, slug: string): Promise<boolean>;
21
25
  /** The artifact's text, or nothing when the issue has none. */
22
26
  export declare function readArtifact(repository: string, slug: string, kind: ArtifactKind): Promise<string | undefined>;
23
27
  /** A draft handed over by an agent, wherever it wrote it. */
24
28
  export declare function readDraft(path: string): Promise<string>;
29
+ export {};
@@ -1,5 +1,7 @@
1
1
  import { access, mkdir, readdir, readFile, stat, writeFile, } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
+ /** An issue's files: a folder with none of them is not an issue. */
4
+ const artifactKinds = ["issue", "spec", "plan"];
3
5
  /** Where the issues are, relative to the repository. */
4
6
  export const issuesPath = join(".factory", "issues");
5
7
  /** `.factory/issues/<slug>/<kind>.md`, relative to the repository: the only place that names these paths. */
@@ -20,7 +22,7 @@ export async function listIssueFolders(repository) {
20
22
  continue;
21
23
  const files = await readdir(join(root, entry.name));
22
24
  // A folder without its files is not an issue (an abandoned one, say).
23
- if (!["issue.md", "spec.md", "plan.md"].some((file) => files.includes(file)))
25
+ if (!artifactKinds.some((kind) => files.includes(`${kind}.md`)))
24
26
  continue;
25
27
  const changes = await Promise.all([".", ...files].map((file) => stat(join(root, entry.name, file)).then(({ mtimeMs }) => mtimeMs,
26
28
  // An editor's temporary file may go between the listing and now.
@@ -53,6 +55,13 @@ export async function writeArtifact(repository, slug, kind, text) {
53
55
  export async function hasArtifact(repository, slug, kind) {
54
56
  return access(join(repository, artifactPath(slug, kind))).then(() => true, () => false);
55
57
  }
58
+ /** Whether `slug` names an issue: a folder with one of its files. */
59
+ export async function isIssue(repository, slug) {
60
+ for (const kind of artifactKinds)
61
+ if (await hasArtifact(repository, slug, kind))
62
+ return true;
63
+ return false;
64
+ }
56
65
  /** The artifact's text, or nothing when the issue has none. */
57
66
  export async function readArtifact(repository, slug, kind) {
58
67
  if (!(await hasArtifact(repository, slug, kind)))
@@ -1 +1 @@
1
- {"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/system/issues.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/system/issues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { CheckResult } from "../domain/verification.js";
2
+ import type { PassedCheck } from "./checks.js";
3
+ /** A check that passed, and where the run kept what it wrote, for the review. */
4
+ export type KeptCheck = CheckResult & {
5
+ outputFile: string;
6
+ };
7
+ /** Keeps `text` in the run's directory, at `path` within it; returns where, absolute. */
8
+ export declare function keepInRun(runDir: string, path: string, text: string): Promise<string>;
9
+ /** Keeps what each check wrote in `checks/<n>-<name>.log` of the run's directory. */
10
+ export declare function keepCheckOutputs(runDir: string, passed: PassedCheck[]): Promise<KeptCheck[]>;
@@ -0,0 +1,21 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ /** Keeps `text` in the run's directory, at `path` within it; returns where, absolute. */
4
+ export async function keepInRun(runDir, path, text) {
5
+ const file = join(runDir, path);
6
+ await mkdir(dirname(file), { recursive: true });
7
+ await writeFile(file, text);
8
+ return file;
9
+ }
10
+ /** Keeps what each check wrote in `checks/<n>-<name>.log` of the run's directory. */
11
+ export function keepCheckOutputs(runDir, passed) {
12
+ return Promise.all(passed.map(async ({ output, ...check }, index) => ({
13
+ ...check,
14
+ outputFile: await keepInRun(runDir, `checks/${index + 1}-${safeName(check.name)}.log`, output),
15
+ })));
16
+ }
17
+ /** A name with only the characters a file name may safely hold. */
18
+ function safeName(name) {
19
+ return name.replace(/[^A-Za-z0-9._-]+/g, "-");
20
+ }
21
+ //# sourceMappingURL=run-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-files.js","sourceRoot":"","sources":["../../src/system/run-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAO1C,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,MAAqB;IAErB,OAAO,OAAO,CAAC,GAAG,CAChB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,GAAG,KAAK;QACR,UAAU,EAAE,MAAM,SAAS,CACzB,MAAM,EACN,UAAU,KAAK,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EACjD,MAAM,CACP;KACF,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clemrig/factory",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Take a repository's issues from spec to pull request with agents, watch them on a local Kanban, and run the repository's own workflows",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@clemrig/factory-ui": "0.1.0",
43
- "@clemrig/flow": "0.5.1",
44
- "@clemrig/flow-ui": "0.4.0"
43
+ "@clemrig/flow": "0.6.0",
44
+ "@clemrig/flow-ui": "0.4.1"
45
45
  }
46
46
  }
@@ -1,17 +1,43 @@
1
- You review a change against its approved spec, independently. You change nothing.
1
+ You review a change against its approved spec, independently. You never change the repository.
2
2
 
3
3
  Read the repository's agent instructions: `AGENTS.md` at the root and in the directories the change touches.
4
- Ignore `.factory/`: it holds other issues' history and agents' outputs, which you must not rely on.
4
+ Ignore `.factory/`, except the files this request names: it holds other issues' history and agents' outputs, which
5
+ you must not rely on.
5
6
 
6
- The request gives the spec, the diff of the change, and the repository's checks, which all passed. Do not take
7
- anything on trust: verify each acceptance criterion in the code and the tests, reading files beyond the diff
8
- when needed. Look for unmet criteria, defects, regressions, missing or weak tests, security problems and
9
- departures from the repository's conventions.
7
+ The request gives the spec, the change and the repository's checks, which all passed:
8
+
9
+ - `changes` lists the changed files with their added and removed lines. A small change comes with its whole diff in
10
+ `diff`; a large one (`large` is true) with `diffFile`, the path of its diff: read the parts you review from it.
11
+ - `checks` gives each check's command, its duration and `outputFile`, where the run kept what it wrote.
12
+
13
+ Do not take anything on trust: verify each acceptance criterion in the code and the tests, reading files beyond the
14
+ diff when needed. Look for unmet criteria, defects, regressions, missing or weak tests, security problems and
15
+ departures from the repository's conventions. Follow every path the change must handle, failures, interruptions
16
+ and recoveries included, not only the one that succeeds.
17
+
18
+ If you can run commands, run what helps you judge: targeted tests, the scripts the change adds, small
19
+ reproductions. Write temporary files only in the system's temporary directory. Never modify, stage, commit or
20
+ delete anything in the repository: the run compares it before and after your review, restores what changed and
21
+ starts the review again.
22
+
23
+ When the change is large and you can launch sub-agents, you may give a few of them, five at most, one angle each
24
+ that this change calls for: for instance its conformance to the spec, its failure and recovery paths, its data and
25
+ migrations, its security or its tests. Give each the spec, its angle, the files concerned and where to read the
26
+ diff, and tell it to report its findings with their evidence, to change nothing and to launch no agent itself.
27
+ Their reports come back to you: check them, then give one answer for the whole change.
10
28
 
11
29
  Answer with:
12
30
 
13
31
  - `criteria`: each acceptance criterion once, `met` or `not_met`, with its evidence (file, test, behavior).
14
- - `findings`: `blocking` for an unmet criterion, a defect or a regression; `minor` for an improvement that is
15
- not required. Give the file when relevant.
32
+ - `findings`: each with its `nature`, its `file` when relevant and a `summary` that states the trigger, then the
33
+ consequence. The nature alone decides whether the finding blocks the change:
34
+ - blocking: `incorrect_behavior` (the change does the wrong thing in a situation it must handle), `data_loss`,
35
+ `outage`, `security`, `regression` (something that worked no longer does), `spec_violation` (the change
36
+ contradicts the spec's scope or constraints);
37
+ - not blocking: `weak_test`, `maintainability`, `documentation`.
38
+
39
+ A defect whose trigger is a situation the change must handle, as the spec and the change's own paths (a
40
+ recovery, say) define them, is `incorrect_behavior` however rare that situation is. Choose the nature that
41
+ describes the defect, not the severity you would like it to have.
16
42
 
17
43
  The change is approved only when every criterion is met and no finding is blocking. Write in English.
@@ -22,10 +22,21 @@ description of the need. Once the operator agrees, save the issue (see Save).
22
22
  2. Ask the operator questions, a few at a time, until you can fill every section without inventing anything.
23
23
  Do not ask what the code or the docs already answer. For each question, give its practical consequence
24
24
  and your recommendation.
25
- 3. Describe what and why, never how: the run that implements the spec plans the technical work itself.
26
- 4. Propose the complete spec and wait for the operator's agreement, then save it (see Save).
25
+ 3. Ask whether the issue relies on another issue that must be delivered first: its spec assumes behavior that
26
+ issue brings. List the issues of `.factory/issues/` that look related, with whether each is delivered (its
27
+ `plan.md` is on the main branch), and record the operator's answer in Dependencies.
28
+ 4. Describe what and why, never how: the run that implements the spec plans the technical work itself.
29
+ 5. Propose the complete spec and wait for the operator's agreement, then save it (see Save).
27
30
 
28
- Other issues in `.factory/issues/` are history, not the current truth: rely on the code and the docs.
31
+ Other issues in `.factory/issues/` are history, not the current truth: rely on the code and the docs, and read
32
+ them only to find the issues this one depends on.
33
+
34
+ ## Fix a refused spec
35
+
36
+ When `factory run implement` refuses to start because of the spec (a missing section such as Dependencies, a
37
+ malformed one, or a dependency that is not delivered), start from the problems it listed: read the spec, ask the
38
+ operator what the fix needs, propose the corrected spec, then save it (see Save). A dependency that is not
39
+ delivered is either delivered first or removed from the spec, as the operator decides.
29
40
 
30
41
  ## Format
31
42
 
@@ -57,13 +68,18 @@ Other issues in `.factory/issues/` are history, not the current truth: rely on t
57
68
 
58
69
  ## Constraints
59
70
  <Limits imposed on the solution>
71
+
72
+ ## Dependencies
73
+ - <slug of an issue that must be delivered first>
60
74
  ```
61
75
 
62
- - Exactly these five sections, in this order, with nothing between the title and the first one.
76
+ - Exactly these six sections, in this order, with nothing between the title and the first one.
63
77
  - No section is empty: write `None.` when there is nothing to say.
64
78
  - Acceptance criteria contains only `- AC<n>: <criterion>` items, numbered from AC1 without gaps; an indented
65
79
  line continues the criterion above it. Each criterion is a fact that a user, an API client or an operator
66
80
  can check, not an implementation detail.
81
+ - Dependencies contains only `- <slug>` items, each an issue of `.factory/issues/` other than this one, or
82
+ `None.`: the run refuses to start until each of them is delivered.
67
83
  - Headings stay in English; the content may be in any language.
68
84
 
69
85
  ## Save
@@ -73,7 +89,8 @@ Other issues in `.factory/issues/` are history, not the current truth: rely on t
73
89
  `factory run issue-save --issue <slug> --kind issue --file <absolute path>` for `issue.md`, or
74
90
  `--kind spec` for `spec.md`.
75
91
  3. `Saved .factory/issues/<slug>/<kind>.md` means it is done. Otherwise the command lists every problem: fix
76
- the draft and run it again. It refuses a spec whose issue does not exist yet.
92
+ the draft and run it again. It refuses a spec whose issue does not exist yet, and a
93
+ dependency that is no issue of the repository.
77
94
 
78
95
  ## Boundaries
79
96