Skip to content

Commit e6b82fd

Browse files
docs(miner-discovery): author MinerGoalSpec schema doc (#2300)
Closes #2300 Add field reference, JSON Schema, README link, and parser round-trip test. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent bfa9c2d commit e6b82fd

4 files changed

Lines changed: 163 additions & 0 deletions

File tree

packages/gittensory-miner/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ no enforcement wiring yet. (#2328)
3030

3131
## Install
3232

33+
See [`docs/miner-goal-spec.md`](docs/miner-goal-spec.md) for the `.gittensory-miner.yml` field reference and [`.gittensory-miner.yml.example`](../../.gittensory-miner.yml.example) at the repo root.
34+
3335
From a local checkout:
3436

3537
```sh
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# MinerGoalSpec (`.gittensory-miner.yml`)
2+
3+
Per-repo configuration telling an autonomous Gittensory miner what to look for and how to behave when targeting a repo. Parsed by `@jsonbored/gittensory-engine` (`parseMinerGoalSpec` / `parseMinerGoalSpecContent`); this document is the field reference. Machine-readable shape: [`../schema/miner-goal-spec.schema.json`](../schema/miner-goal-spec.schema.json). Copy [`.gittensory-miner.yml.example`](../../../.gittensory-miner.yml.example) to `.gittensory-miner.yml` and edit.
4+
5+
Discovery order (first match wins):
6+
7+
- `.gittensory-miner.yml`
8+
- `.github/gittensory-miner.yml`
9+
- `.gittensory-miner.json`
10+
- `.github/gittensory-miner.json`
11+
12+
Every field is optional. Unknown keys are ignored; a malformed field falls back to its documented default with a warning — a broken file never hard-fails the miner.
13+
14+
## Relationship to `.gittensory.yml`
15+
16+
| File | Actor | Purpose |
17+
|------|-------|---------|
18+
| `.gittensory.yml` | Review stack | How a maintainer's repo **reviews** incoming PRs (focus manifest, gate, scoring knobs). |
19+
| `.gittensory-miner.yml` | Miner runtime | How a miner **searches for and prioritizes** work in a target repo. |
20+
21+
They are read by different components and do not conflict. A miner should still treat a target repo's public `.gittensory.yml` `wantedPaths` / `blockedPaths` as a hard floor when both files exist.
22+
23+
## Fields
24+
25+
### `minerEnabled` (boolean, default: `true`)
26+
27+
Explicit opt-out: a public repo with no file remains minable. Set `false` to halt all miner targeting.
28+
29+
### `wantedPaths` (string list, default: `[]`)
30+
31+
Work areas the maintainer wants a miner to focus on. Glob list. Empty means no preference.
32+
33+
### `blockedPaths` (string list, default: `[]`)
34+
35+
Paths off-limits to a miner; candidates touching one should be skipped. Glob list. Mirrors `.gittensory.yml` `blockedPaths` semantics.
36+
37+
### `preferredLabels` (string list, default: `[]`)
38+
39+
Issue labels a miner should favor. Empty means no preference.
40+
41+
### `blockedLabels` (string list, default: `[]`)
42+
43+
Issue labels a miner must skip.
44+
45+
### `maxConcurrentClaims` (integer `>= 1`, default: `1`)
46+
47+
Maximum issues one miner may hold claimed on this repo at once.
48+
49+
### `issueDiscoveryPolicy` (`encouraged` | `neutral` | `discouraged`, default: `neutral`)
50+
51+
How strongly this repo encourages a miner to open discovery issues.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://jsonbored.dev/schemas/gittensory-miner-goal-spec.json",
4+
"title": "MinerGoalSpec",
5+
"description": "Per-repo miner configuration parsed from `.gittensory-miner.yml` (see packages/gittensory-miner/docs/miner-goal-spec.md).",
6+
"type": "object",
7+
"additionalProperties": true,
8+
"properties": {
9+
"minerEnabled": {
10+
"type": "boolean",
11+
"default": true,
12+
"description": "Whether autonomous miners may target this repo. Default: true."
13+
},
14+
"wantedPaths": {
15+
"type": "array",
16+
"items": { "type": "string", "minLength": 1 },
17+
"default": [],
18+
"description": "Work areas a miner should prefer. Glob list. Default: []."
19+
},
20+
"blockedPaths": {
21+
"type": "array",
22+
"items": { "type": "string", "minLength": 1 },
23+
"default": [],
24+
"description": "Paths a miner must skip. Glob list. Default: []."
25+
},
26+
"preferredLabels": {
27+
"type": "array",
28+
"items": { "type": "string", "minLength": 1 },
29+
"default": [],
30+
"description": "Issue labels a miner should favor. Default: []."
31+
},
32+
"blockedLabels": {
33+
"type": "array",
34+
"items": { "type": "string", "minLength": 1 },
35+
"default": [],
36+
"description": "Issue labels a miner must skip. Default: []."
37+
},
38+
"maxConcurrentClaims": {
39+
"type": "integer",
40+
"minimum": 1,
41+
"default": 1,
42+
"description": "Maximum active claims one miner may hold on this repo. Default: 1."
43+
},
44+
"issueDiscoveryPolicy": {
45+
"type": "string",
46+
"enum": ["encouraged", "neutral", "discouraged"],
47+
"default": "neutral",
48+
"description": "How strongly opening discovery issues is encouraged. Default: neutral."
49+
}
50+
}
51+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { readFileSync } from "node:fs";
2+
import { join } from "node:path";
3+
import { describe, expect, it } from "vitest";
4+
5+
import { parseMinerGoalSpecContent } from "../../packages/gittensory-engine/src/miner-goal-spec";
6+
7+
const repoRoot = process.cwd();
8+
const schemaPath = join(repoRoot, "packages/gittensory-miner/schema/miner-goal-spec.schema.json");
9+
const docPath = join(repoRoot, "packages/gittensory-miner/docs/miner-goal-spec.md");
10+
const examplePath = join(repoRoot, ".gittensory-miner.yml.example");
11+
12+
const SPEC_FIELDS = [
13+
"minerEnabled",
14+
"wantedPaths",
15+
"blockedPaths",
16+
"preferredLabels",
17+
"blockedLabels",
18+
"maxConcurrentClaims",
19+
"issueDiscoveryPolicy",
20+
] as const;
21+
22+
describe("miner goal spec docs (#2300)", () => {
23+
it("documents every MinerGoalSpec field and the relationship to .gittensory.yml", () => {
24+
const doc = readFileSync(docPath, "utf8");
25+
expect(doc).toContain("Relationship to `.gittensory.yml`");
26+
expect(doc).toContain("wantedPaths");
27+
expect(doc).toContain("blockedPaths");
28+
for (const field of SPEC_FIELDS) {
29+
expect(doc).toContain(field);
30+
}
31+
});
32+
33+
it("ships a JSON Schema draft 2020-12 with the MinerGoalSpec properties", () => {
34+
const schema = JSON.parse(readFileSync(schemaPath, "utf8")) as {
35+
$schema: string;
36+
properties: Record<string, unknown>;
37+
};
38+
expect(schema.$schema).toBe("https://json-schema.org/draft/2020-12/schema");
39+
for (const field of SPEC_FIELDS) {
40+
expect(schema.properties).toHaveProperty(field);
41+
}
42+
});
43+
44+
it("parses the root example file through the engine parser", () => {
45+
const example = readFileSync(examplePath, "utf8");
46+
const parsed = parseMinerGoalSpecContent(example, ".gittensory-miner.yml.example");
47+
expect(parsed.present).toBe(true);
48+
expect(parsed.spec).toMatchObject({
49+
minerEnabled: true,
50+
wantedPaths: ["src/**"],
51+
blockedPaths: ["vendor/**", ".github/workflows/**"],
52+
preferredLabels: ["bug", "enhancement"],
53+
blockedLabels: ["wontfix", "duplicate"],
54+
maxConcurrentClaims: 1,
55+
issueDiscoveryPolicy: "neutral",
56+
});
57+
expect(parsed.warnings).toEqual([]);
58+
});
59+
});

0 commit comments

Comments
 (0)