@homeostat/core 0.1.4 → 0.1.6
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/src/automation.d.ts
CHANGED
|
@@ -51,9 +51,16 @@ export interface RunReport {
|
|
|
51
51
|
dailyPr?: string;
|
|
52
52
|
reviewPr?: string;
|
|
53
53
|
mergeError?: string;
|
|
54
|
+
/** Why the host refused to request the reviewers, e.g. they are not collaborators. */
|
|
55
|
+
reviewError?: string;
|
|
54
56
|
}[];
|
|
55
57
|
/** Review PRs from earlier runs that a reviewer merged or closed since. */
|
|
56
58
|
settled: number;
|
|
59
|
+
/** Review threads read this run that gave no lesson, and why (one-off, skip marker, too long...). */
|
|
60
|
+
skipped?: {
|
|
61
|
+
url: string;
|
|
62
|
+
reason: string;
|
|
63
|
+
}[];
|
|
57
64
|
lessons: {
|
|
58
65
|
member: string;
|
|
59
66
|
id: string;
|
|
@@ -107,7 +107,7 @@ export async function curate(input) {
|
|
|
107
107
|
}
|
|
108
108
|
const { thread: safe, redacted } = redactThread(thread);
|
|
109
109
|
result.redacted += redacted;
|
|
110
|
-
|
|
110
|
+
let draft = await input.drafter.draft(safe, { areas: config.areas });
|
|
111
111
|
if (input.patcher) {
|
|
112
112
|
await docPatchStep({ files, config, result, today: input.today }, input.patcher, safe, url);
|
|
113
113
|
}
|
|
@@ -115,17 +115,34 @@ export async function curate(input) {
|
|
|
115
115
|
skip("no lasting rule in the thread");
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
const check = (d) => {
|
|
119
|
+
const area = d.area === CORE_AREA || config.areas[d.area] ? d.area : CORE_AREA;
|
|
120
|
+
const built = buildEntry(config, {
|
|
121
|
+
draft: { ...d, area },
|
|
122
|
+
id: uniqueId(slugify(d.title) ?? `review-${pr.id}-${thread.root.id}`, ids),
|
|
123
|
+
url,
|
|
124
|
+
author: reviewer.id,
|
|
125
|
+
today: input.today,
|
|
126
|
+
});
|
|
127
|
+
const errs = validate({ ...files, [built.path]: built.text }).issues.filter((i) => i.file === built.path && i.severity === "error");
|
|
128
|
+
return { built, errs };
|
|
129
|
+
};
|
|
127
130
|
const isRisk = (i) => i.code.startsWith("risk.");
|
|
128
|
-
|
|
131
|
+
let { built: entry, errs: errors } = check(draft);
|
|
132
|
+
let blocking = errors.filter((i) => !isRisk(i));
|
|
133
|
+
// A draft over a length limit is worth one rewrite rather than losing the lesson.
|
|
134
|
+
if (blocking.length > 0 && blocking.every(isLengthIssue)) {
|
|
135
|
+
const problems = blocking.map((i) => i.message);
|
|
136
|
+
const second = await input.drafter.draft(safe, {
|
|
137
|
+
areas: config.areas,
|
|
138
|
+
revise: { previous: draft, problems },
|
|
139
|
+
});
|
|
140
|
+
if (second) {
|
|
141
|
+
draft = second;
|
|
142
|
+
({ built: entry, errs: errors } = check(draft));
|
|
143
|
+
blocking = errors.filter((i) => !isRisk(i));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
129
146
|
if (blocking.length > 0) {
|
|
130
147
|
skip(`draft did not validate: ${blocking.map((i) => i.message).join("; ")}`);
|
|
131
148
|
continue;
|
|
@@ -214,6 +231,8 @@ async function docPatchStep(ctx, patcher, thread, url) {
|
|
|
214
231
|
ctx.result.docs.push({ path: proposal.path, text, sections: [section], risks });
|
|
215
232
|
}
|
|
216
233
|
}
|
|
234
|
+
/** Over a word or character limit: fixable by rewriting shorter. */
|
|
235
|
+
const isLengthIssue = (i) => i.code.endsWith(".tooLong") || /at most \d+ characters/.test(i.message);
|
|
217
236
|
function uniqueId(base, taken) {
|
|
218
237
|
let id = base;
|
|
219
238
|
for (let n = 2; taken.has(id); n++)
|
|
@@ -10,6 +10,11 @@ export interface Draft {
|
|
|
10
10
|
export interface DraftContext {
|
|
11
11
|
/** Area name → path globs, from homeostat.yaml. `core` is always available. */
|
|
12
12
|
areas: Record<string, readonly string[]>;
|
|
13
|
+
/** A second try: the previous draft and the limits it broke. */
|
|
14
|
+
revise?: {
|
|
15
|
+
previous: Draft;
|
|
16
|
+
problems: string[];
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
/** Turns one review thread into at most one rule; null when the thread holds no lasting lesson. */
|
|
15
20
|
export interface Drafter {
|
|
@@ -37,7 +37,19 @@ ${conversation}
|
|
|
37
37
|
</thread>
|
|
38
38
|
|
|
39
39
|
Areas and the paths they cover:
|
|
40
|
-
${areas.map((a) => `- ${a}`).join("\n")}
|
|
40
|
+
${areas.map((a) => `- ${a}`).join("\n")}${ctx.revise
|
|
41
|
+
? `
|
|
42
|
+
|
|
43
|
+
Your previous draft broke these limits:
|
|
44
|
+
${ctx.revise.problems.map((p) => `- ${p}`).join("\n")}
|
|
45
|
+
|
|
46
|
+
Previous draft:
|
|
47
|
+
Title: ${ctx.revise.previous.title}
|
|
48
|
+
Rule: ${ctx.revise.previous.rule}
|
|
49
|
+
Why: ${ctx.revise.previous.why}
|
|
50
|
+
|
|
51
|
+
Write it again within the limits, keeping every name and fact it states.`
|
|
52
|
+
: ""}`;
|
|
41
53
|
}
|
|
42
54
|
/** Drafts with Claude through either runtime (API or Claude Code). */
|
|
43
55
|
export class ClaudeDrafter {
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The release version; `scripts/version.mjs` keeps it in step with every package.json. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.6";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The release version; `scripts/version.mjs` keeps it in step with every package.json. */
|
|
2
|
-
export const VERSION = "0.1.
|
|
2
|
+
export const VERSION = "0.1.6";
|