@telora/daemon 0.24.2 → 0.24.5
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/build-info.json +8 -3
- package/dist/backends/claude/one-shot-model.d.ts.map +1 -1
- package/dist/backends/claude/one-shot-model.js +2 -0
- package/dist/backends/claude/one-shot-model.js.map +1 -1
- package/dist/ci/ci-status.d.ts.map +1 -1
- package/dist/ci/ci-status.js +1 -0
- package/dist/ci/ci-status.js.map +1 -1
- package/dist/ci/product-ci-repo.d.ts.map +1 -1
- package/dist/ci/product-ci-repo.js +3 -1
- package/dist/ci/product-ci-repo.js.map +1 -1
- package/dist/completion/state-cascade/review-approved-head-gate.d.ts.map +1 -1
- package/dist/completion/state-cascade/review-approved-head-gate.js +2 -0
- package/dist/completion/state-cascade/review-approved-head-gate.js.map +1 -1
- package/dist/completion/state-cascade/review-capture-head-gate.d.ts +28 -0
- package/dist/completion/state-cascade/review-capture-head-gate.d.ts.map +1 -0
- package/dist/completion/state-cascade/review-capture-head-gate.js +48 -0
- package/dist/completion/state-cascade/review-capture-head-gate.js.map +1 -0
- package/dist/completion/state-cascade/state-cascade.d.ts +12 -0
- package/dist/completion/state-cascade/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade/state-cascade.js +19 -10
- package/dist/completion/state-cascade/state-cascade.js.map +1 -1
- package/dist/composition/daemon-contexts.d.ts.map +1 -1
- package/dist/composition/daemon-contexts.js +3 -1
- package/dist/composition/daemon-contexts.js.map +1 -1
- package/dist/composition/execution-git-context.d.ts.map +1 -1
- package/dist/composition/execution-git-context.js +1 -0
- package/dist/composition/execution-git-context.js.map +1 -1
- package/dist/composition/execution-worktree-context.js +1 -1
- package/dist/composition/release-note-pass-context.d.ts +7 -0
- package/dist/composition/release-note-pass-context.d.ts.map +1 -0
- package/dist/composition/release-note-pass-context.js +64 -0
- package/dist/composition/release-note-pass-context.js.map +1 -0
- package/dist/composition/runtime-contexts.d.ts.map +1 -1
- package/dist/composition/runtime-contexts.js +2 -0
- package/dist/composition/runtime-contexts.js.map +1 -1
- package/dist/composition/spawn-pass-deps.d.ts +2 -2
- package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
- package/dist/composition/spawn-pass-deps.js +3 -1
- package/dist/composition/spawn-pass-deps.js.map +1 -1
- package/dist/composition/state-cascade-context.d.ts.map +1 -1
- package/dist/composition/state-cascade-context.js +2 -0
- package/dist/composition/state-cascade-context.js.map +1 -1
- package/dist/config/live-daemon-config.js +1 -1
- package/dist/daemon-state/provider-window.d.ts.map +1 -1
- package/dist/daemon-state/provider-window.js +1 -0
- package/dist/daemon-state/provider-window.js.map +1 -1
- package/dist/directive/directive-queue.js +1 -1
- package/dist/focus/daemon-contexts.d.ts +3 -0
- package/dist/focus/daemon-contexts.d.ts.map +1 -1
- package/dist/focus/daemon-contexts.js.map +1 -1
- package/dist/focus/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus/focus-loop-registry.js +21 -0
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/pending-review-approved-head.d.ts +5 -1
- package/dist/focus/pending-review-approved-head.d.ts.map +1 -1
- package/dist/focus/pending-review-approved-head.js +35 -1
- package/dist/focus/pending-review-approved-head.js.map +1 -1
- package/dist/focus/review-spawn-coverage.d.ts +11 -0
- package/dist/focus/review-spawn-coverage.d.ts.map +1 -1
- package/dist/focus/review-spawn-coverage.js +14 -0
- package/dist/focus/review-spawn-coverage.js.map +1 -1
- package/dist/git/delivery-trailer.d.ts.map +1 -1
- package/dist/git/delivery-trailer.js +2 -0
- package/dist/git/delivery-trailer.js.map +1 -1
- package/dist/git/utils.d.ts.map +1 -1
- package/dist/git/utils.js +2 -1
- package/dist/git/utils.js.map +1 -1
- package/dist/merge/candidate-rework.d.ts.map +1 -1
- package/dist/merge/candidate-rework.js +24 -2
- package/dist/merge/candidate-rework.js.map +1 -1
- package/dist/merge/completion-merge-activity.d.ts.map +1 -1
- package/dist/merge/completion-merge-activity.js +7 -1
- package/dist/merge/completion-merge-activity.js.map +1 -1
- package/dist/merge/completion-merge-driver.d.ts +15 -5
- package/dist/merge/completion-merge-driver.d.ts.map +1 -1
- package/dist/merge/completion-merge-driver.js +7 -2
- package/dist/merge/completion-merge-driver.js.map +1 -1
- package/dist/merge/completion-merge-gate.d.ts +5 -1
- package/dist/merge/completion-merge-gate.d.ts.map +1 -1
- package/dist/merge/completion-merge-gate.js +23 -7
- package/dist/merge/completion-merge-gate.js.map +1 -1
- package/dist/merge/merge-back-loop.d.ts.map +1 -1
- package/dist/merge/merge-back-loop.js +2 -1
- package/dist/merge/merge-back-loop.js.map +1 -1
- package/dist/merge/merge-gate-coverage.d.ts +0 -2
- package/dist/merge/merge-gate-coverage.d.ts.map +1 -1
- package/dist/merge/merge-gate-coverage.js +11 -9
- package/dist/merge/merge-gate-coverage.js.map +1 -1
- package/dist/prompt-sections/release-note-faithfulness-section.d.ts +28 -0
- package/dist/prompt-sections/release-note-faithfulness-section.d.ts.map +1 -0
- package/dist/prompt-sections/release-note-faithfulness-section.js +99 -0
- package/dist/prompt-sections/release-note-faithfulness-section.js.map +1 -0
- package/dist/prompt-sections/release-note-section.d.ts +58 -0
- package/dist/prompt-sections/release-note-section.d.ts.map +1 -0
- package/dist/prompt-sections/release-note-section.js +285 -0
- package/dist/prompt-sections/release-note-section.js.map +1 -0
- package/dist/queries/api-client/shared.d.ts +1 -0
- package/dist/queries/api-client/shared.d.ts.map +1 -1
- package/dist/queries/api-client/shared.js +16 -1
- package/dist/queries/api-client/shared.js.map +1 -1
- package/dist/queries/release-notes/release-notes.d.ts +242 -0
- package/dist/queries/release-notes/release-notes.d.ts.map +1 -0
- package/dist/queries/release-notes/release-notes.js +148 -0
- package/dist/queries/release-notes/release-notes.js.map +1 -0
- package/dist/release-notes/release-note-author.d.ts +56 -0
- package/dist/release-notes/release-note-author.d.ts.map +1 -0
- package/dist/release-notes/release-note-author.js +107 -0
- package/dist/release-notes/release-note-author.js.map +1 -0
- package/dist/release-notes/release-note-faithfulness.d.ts +40 -0
- package/dist/release-notes/release-note-faithfulness.d.ts.map +1 -0
- package/dist/release-notes/release-note-faithfulness.js +107 -0
- package/dist/release-notes/release-note-faithfulness.js.map +1 -0
- package/dist/release-notes/release-note-pass.d.ts +77 -0
- package/dist/release-notes/release-note-pass.d.ts.map +1 -0
- package/dist/release-notes/release-note-pass.js +201 -0
- package/dist/release-notes/release-note-pass.js.map +1 -0
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.d.ts +4 -3
- package/dist/testing/fixtures/approved-head-inert-deps.d.ts.map +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.js +3 -0
- package/dist/testing/fixtures/approved-head-inert-deps.js.map +1 -1
- package/dist/testing/fixtures/completion-judge-stubs.d.ts.map +1 -1
- package/dist/testing/fixtures/completion-judge-stubs.js +2 -0
- package/dist/testing/fixtures/completion-judge-stubs.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
- package/dist/testing/fixtures/pending-spawn-world.js +1 -0
- package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/worktree/capture-head-adoption.d.ts +44 -0
- package/dist/worktree/capture-head-adoption.d.ts.map +1 -0
- package/dist/worktree/capture-head-adoption.js +96 -0
- package/dist/worktree/capture-head-adoption.js.map +1 -0
- package/dist/worktree/safety.d.ts +12 -3
- package/dist/worktree/safety.d.ts.map +1 -1
- package/dist/worktree/safety.js +15 -3
- package/dist/worktree/safety.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/composition/release-note-pass-context.test.ts,
|
|
3
|
+
// packages/daemon/src/prompt-sections/release-note-section.test.ts,
|
|
4
|
+
// packages/daemon/src/queries/release-notes/release-notes.test.ts,
|
|
5
|
+
// packages/daemon/src/release-notes/release-note-author.test.ts,
|
|
6
|
+
// packages/daemon/src/release-notes/release-note-faithfulness.test.ts,
|
|
7
|
+
// packages/daemon/src/release-notes/release-note-pass.test.ts
|
|
8
|
+
// PINNED-BY-END
|
|
9
|
+
/**
|
|
10
|
+
* The release-note drafting pass's reads and writes (product-api
|
|
11
|
+
* daemon_get_release_note_candidates / daemon_release_note_claim /
|
|
12
|
+
* daemon_release_note_write; the edge side is
|
|
13
|
+
* supabase/functions/product-api/handlers/daemon-release-note-handlers.ts and
|
|
14
|
+
* the drafting context release-drafting.ts). All three ride the isolated
|
|
15
|
+
* `releaseNotes` circuit breaker, so a failing drafting pass never opens a
|
|
16
|
+
* breaker armed work reads or writes through.
|
|
17
|
+
*
|
|
18
|
+
* A claim answers the DRAFTING CONTEXT: every piece of work the release
|
|
19
|
+
* carries, in its own engineering words (the source material, and the internal
|
|
20
|
+
* names the note must not repeat), the radar blips and themes that informed it,
|
|
21
|
+
* and the links a user reaches it by in the release's environment.
|
|
22
|
+
*
|
|
23
|
+
* Every outcome is written under the claim it was made with. A draft the edge
|
|
24
|
+
* refuses comes back as an ApiError: HTTP 400 `validation_failed` (the note is
|
|
25
|
+
* not in the format, or not faithful) or HTTP 409 `state_conflict` (the claim
|
|
26
|
+
* was lost, or the human changed the release's work while it was drafted).
|
|
27
|
+
* readReleaseNoteWriteFailure is the one reader of those.
|
|
28
|
+
*
|
|
29
|
+
* WHAT THE DAEMON CAN READ OF A REFUSAL. The edge's 400 body carries every
|
|
30
|
+
* violation in a `violations` array, but the daemon's API transport
|
|
31
|
+
* (@telora/daemon-core createApiClient -> ApiError) keeps only the body's
|
|
32
|
+
* `error` text, the HTTP status and the `code`. The refusal's violations are
|
|
33
|
+
* therefore read from the error TEXT, verbatim, as one entry -- whatever the
|
|
34
|
+
* edge writes there is what the corrective attempt is shown.
|
|
35
|
+
*/
|
|
36
|
+
import { z } from 'zod';
|
|
37
|
+
import { callApiValidated } from '../api-client/shared.js';
|
|
38
|
+
const nullableString = () => z.string().nullable().optional().default(null);
|
|
39
|
+
const ReleaseNoteCandidateSchema = z.object({
|
|
40
|
+
releaseId: z.string(),
|
|
41
|
+
productId: z.string(),
|
|
42
|
+
name: z.string(),
|
|
43
|
+
cutAt: z.string(),
|
|
44
|
+
}).passthrough();
|
|
45
|
+
const ReleaseNoteCandidatesSchema = z.object({
|
|
46
|
+
releases: z.array(ReleaseNoteCandidateSchema),
|
|
47
|
+
}).passthrough();
|
|
48
|
+
const DraftingLinkSchema = z.object({
|
|
49
|
+
/** Absolute https: the environment's address joined to a QA case path. */
|
|
50
|
+
url: z.string(),
|
|
51
|
+
built: z.string().optional().default(''),
|
|
52
|
+
criterion: z.string().optional().default(''),
|
|
53
|
+
expected: z.string().optional().default(''),
|
|
54
|
+
}).passthrough();
|
|
55
|
+
const DraftingWorkSchema = z.object({
|
|
56
|
+
deliveryId: z.string(),
|
|
57
|
+
title: z.string(),
|
|
58
|
+
description: nullableString(),
|
|
59
|
+
acceptanceCriteria: nullableString(),
|
|
60
|
+
workFacing: nullableString(),
|
|
61
|
+
focus: z.object({
|
|
62
|
+
focusId: z.string(),
|
|
63
|
+
title: z.string(),
|
|
64
|
+
description: nullableString(),
|
|
65
|
+
}).passthrough().nullable().optional().default(null),
|
|
66
|
+
issues: z.array(z.object({ title: z.string(), issueType: nullableString() }).passthrough()).optional().default([]),
|
|
67
|
+
radar: z.object({
|
|
68
|
+
blips: z.array(z.object({ title: z.string(), category: z.string(), assessment: z.string() }).passthrough()).optional().default([]),
|
|
69
|
+
themes: z.array(z.object({ title: z.string(), assessment: z.string() }).passthrough()).optional().default([]),
|
|
70
|
+
}).passthrough().optional().default({ blips: [], themes: [] }),
|
|
71
|
+
links: z.array(DraftingLinkSchema).optional().default([]),
|
|
72
|
+
}).passthrough();
|
|
73
|
+
const DraftingContextSchema = z.object({
|
|
74
|
+
releaseId: z.string(),
|
|
75
|
+
productId: z.string(),
|
|
76
|
+
productName: z.string(),
|
|
77
|
+
releaseName: z.string(),
|
|
78
|
+
/** The release date, YYYY-MM-DD. */
|
|
79
|
+
date: z.string(),
|
|
80
|
+
environment: z.object({ name: z.string(), address: nullableString() }).passthrough(),
|
|
81
|
+
work: z.array(DraftingWorkSchema),
|
|
82
|
+
/** Focus, delivery and issue titles: internal names no note may repeat. */
|
|
83
|
+
internalNames: z.array(z.string()).optional().default([]),
|
|
84
|
+
}).passthrough();
|
|
85
|
+
const ReleaseNoteClaimSchema = z.discriminatedUnion('claimed', [
|
|
86
|
+
z.object({ claimed: z.literal(false), reason: z.string().optional().default('') }).passthrough(),
|
|
87
|
+
z.object({ claimed: z.literal(true), claimId: z.string(), context: DraftingContextSchema }).passthrough(),
|
|
88
|
+
]);
|
|
89
|
+
const ReleaseNoteWriteResultSchema = z.object({
|
|
90
|
+
release: z.unknown().optional(),
|
|
91
|
+
released: z.boolean().optional(),
|
|
92
|
+
}).passthrough();
|
|
93
|
+
/** The product's releases still drafting and held by no live pass, oldest cut first (bounded by the edge). */
|
|
94
|
+
export async function getReleaseNoteCandidates(productId) {
|
|
95
|
+
const result = await callApiValidated('daemon_get_release_note_candidates', ReleaseNoteCandidatesSchema, { productId });
|
|
96
|
+
return result.releases;
|
|
97
|
+
}
|
|
98
|
+
/** Take one release to draft. Not claimed when it stopped drafting or another pass holds it. */
|
|
99
|
+
export async function claimReleaseNote(releaseId) {
|
|
100
|
+
return callApiValidated('daemon_release_note_claim', ReleaseNoteClaimSchema, { releaseId });
|
|
101
|
+
}
|
|
102
|
+
/** Write the draft or the failure under the claim. Throws on a refusal (readReleaseNoteWriteFailure reads it). */
|
|
103
|
+
export async function writeReleaseNote(write) {
|
|
104
|
+
await callApiValidated('daemon_release_note_write', ReleaseNoteWriteResultSchema, { ...write });
|
|
105
|
+
}
|
|
106
|
+
/** Give the claim back so the next tick can draft the release again; false when this pass no longer held it. */
|
|
107
|
+
export async function unclaimReleaseNote(releaseId, claimId) {
|
|
108
|
+
const result = await callApiValidated('daemon_release_note_write', ReleaseNoteWriteResultSchema, {
|
|
109
|
+
releaseId, claimId, outcome: 'unclaim',
|
|
110
|
+
});
|
|
111
|
+
return result.released === true;
|
|
112
|
+
}
|
|
113
|
+
function errorMessage(err) {
|
|
114
|
+
return err instanceof Error ? err.message : String(err);
|
|
115
|
+
}
|
|
116
|
+
/** The edge's refusal text: this first line, then one "- <violation>" line per violation. */
|
|
117
|
+
const REFUSAL_PREFIX = 'the note was refused:';
|
|
118
|
+
/**
|
|
119
|
+
* The violations a refusal names. The edge writes each one into its message,
|
|
120
|
+
* one per line (the api client keeps the message, not the body), so they are
|
|
121
|
+
* read back from it; a message in any other shape is kept whole as one
|
|
122
|
+
* violation.
|
|
123
|
+
*/
|
|
124
|
+
export function refusalViolations(message) {
|
|
125
|
+
if (!message.startsWith(REFUSAL_PREFIX))
|
|
126
|
+
return [message];
|
|
127
|
+
const parts = message
|
|
128
|
+
.slice(REFUSAL_PREFIX.length)
|
|
129
|
+
.split('\n')
|
|
130
|
+
.map((line) => line.trim().replace(/^- /, '').trim())
|
|
131
|
+
.filter((line) => line.length > 0);
|
|
132
|
+
return parts.length > 0 ? parts : [message];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Classify what a release-note write threw, from the edge envelope's HTTP
|
|
136
|
+
* status and machine code (ApiError.statusCode / wireCode), never its prose.
|
|
137
|
+
* Total: every input yields a failure. Pure.
|
|
138
|
+
*/
|
|
139
|
+
export function readReleaseNoteWriteFailure(err) {
|
|
140
|
+
const { statusCode, wireCode } = (err ?? {});
|
|
141
|
+
const detail = errorMessage(err);
|
|
142
|
+
if (statusCode === 400 && wireCode === 'validation_failed')
|
|
143
|
+
return { kind: 'refused', violations: refusalViolations(detail) };
|
|
144
|
+
if (statusCode === 409)
|
|
145
|
+
return { kind: 'conflict', detail };
|
|
146
|
+
return { kind: 'error', detail };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=release-notes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-notes.js","sourceRoot":"","sources":["../../../src/queries/release-notes/release-notes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DraftingContext, ReleaseNoteDraft } from '../queries/release-notes/release-notes.js';
|
|
2
|
+
/** Each attempt's wall-clock bound. */
|
|
3
|
+
export declare const RELEASE_NOTE_TIMEOUT_MS: number;
|
|
4
|
+
/** The turn cap (the pass has no tools: everything it knows is in its prompt). */
|
|
5
|
+
export declare const RELEASE_NOTE_MAX_TURNS = 3;
|
|
6
|
+
/** One model run, as the author asks for it. */
|
|
7
|
+
export interface ReleaseNoteInvokeOptions {
|
|
8
|
+
label: string;
|
|
9
|
+
extraArgs: readonly string[];
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
maxTurns: number;
|
|
12
|
+
}
|
|
13
|
+
/** Run the model once; resolve with its final answer text, or reject (a ModelUnavailable names why). */
|
|
14
|
+
export type ReleaseNoteInvoke = (systemPrompt: string, userMessage: string, opts: ReleaseNoteInvokeOptions) => Promise<{
|
|
15
|
+
text: string;
|
|
16
|
+
}>;
|
|
17
|
+
export interface ReleaseNoteAuthorDeps {
|
|
18
|
+
invoke: ReleaseNoteInvoke;
|
|
19
|
+
}
|
|
20
|
+
/** What the corrective attempt is shown: the refused note and why it was refused. */
|
|
21
|
+
export interface ReleaseNoteCorrection {
|
|
22
|
+
previous: ReleaseNoteDraft;
|
|
23
|
+
violations: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export type ReleaseNoteAuthorResult = {
|
|
26
|
+
kind: 'note';
|
|
27
|
+
note: ReleaseNoteDraft;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'failed';
|
|
30
|
+
reason: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'held';
|
|
33
|
+
reason: string;
|
|
34
|
+
};
|
|
35
|
+
export type ReleaseNoteOutput = {
|
|
36
|
+
kind: 'note';
|
|
37
|
+
note: ReleaseNoteDraft;
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'error';
|
|
40
|
+
error: string;
|
|
41
|
+
};
|
|
42
|
+
/** The argv the pass adds to the one-shot argv: no tools, no MCP servers. Pure. */
|
|
43
|
+
export declare function buildReleaseNoteArgs(): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Parse a release-note answer: the last fenced json block, holding a note
|
|
46
|
+
* object. Only the note's own fields are kept (a title is the release's own,
|
|
47
|
+
* set by the edge). Pure.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseReleaseNoteOutput(text: string): ReleaseNoteOutput;
|
|
50
|
+
/**
|
|
51
|
+
* Author one release's note -- or, given a correction, the whole note again
|
|
52
|
+
* with the refused note and its violations appended. Never throws for a model
|
|
53
|
+
* or parse problem (those are results).
|
|
54
|
+
*/
|
|
55
|
+
export declare function authorReleaseNote(context: DraftingContext, deps: ReleaseNoteAuthorDeps, correction?: ReleaseNoteCorrection | null): Promise<ReleaseNoteAuthorResult>;
|
|
56
|
+
//# sourceMappingURL=release-note-author.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-note-author.d.ts","sourceRoot":"","sources":["../../src/release-notes/release-note-author.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAOnG,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,QAAc,CAAC;AACnD,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,gDAAgD;AAChD,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wGAAwG;AACxG,MAAM,MAAM,iBAAiB,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEzI,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,qFAAqF;AACrF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5G,mFAAmF;AACnF,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAQD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAmBtE;AAMD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,qBAAqB,EAC3B,UAAU,GAAE,qBAAqB,GAAG,IAAW,GAC9C,OAAO,CAAC,uBAAuB,CAAC,CAsBlC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/composition/release-note-pass-context.test.ts,
|
|
3
|
+
// packages/daemon/src/prompt-sections/release-note-section.test.ts,
|
|
4
|
+
// packages/daemon/src/release-notes/release-note-author.test.ts,
|
|
5
|
+
// packages/daemon/src/release-notes/release-note-faithfulness.test.ts,
|
|
6
|
+
// packages/daemon/src/release-notes/release-note-pass.test.ts
|
|
7
|
+
// PINNED-BY-END
|
|
8
|
+
/**
|
|
9
|
+
* Author one release's note: a FRESH one-shot model run (no tools, no MCP
|
|
10
|
+
* servers, a three-turn cap, ten minutes) over the drafting context the claim
|
|
11
|
+
* answered, read back as the LAST fenced ```json block of the answer.
|
|
12
|
+
*
|
|
13
|
+
* The check here is structural only -- a summary, a non-empty list of item
|
|
14
|
+
* objects -- so an answer that could never be a note is caught before it is
|
|
15
|
+
* written. The format (src/lib/releases/release-note-format.ts) and the
|
|
16
|
+
* faithfulness rules (sources name release work, links are ones offered for
|
|
17
|
+
* that work, no internal names, ids or commits) are the edge's: a draft it
|
|
18
|
+
* refuses comes back to the pass, which asks ONCE more with the refusal
|
|
19
|
+
* appended (a `correction`). Items are sent as the model wrote them, so the
|
|
20
|
+
* edge's refusal names exactly what to fix.
|
|
21
|
+
*
|
|
22
|
+
* A provider rate limit is not a failure of the pass: it returns `held`, and
|
|
23
|
+
* the pass gives the claim back so a later tick drafts the release. Every other
|
|
24
|
+
* problem is `failed` with its reason. The model run is injected
|
|
25
|
+
* (ReleaseNoteAuthorDeps.invoke), so no test needs a claude binary.
|
|
26
|
+
*/
|
|
27
|
+
import { isModelUnavailable } from '../backends/claude/one-shot-model.js';
|
|
28
|
+
import { buildReleaseNoteCorrection, buildReleaseNotePrompt, RELEASE_NOTE_SYSTEM_PROMPT, } from '../prompt-sections/release-note-section.js';
|
|
29
|
+
/** Each attempt's wall-clock bound. */
|
|
30
|
+
export const RELEASE_NOTE_TIMEOUT_MS = 10 * 60_000;
|
|
31
|
+
/** The turn cap (the pass has no tools: everything it knows is in its prompt). */
|
|
32
|
+
export const RELEASE_NOTE_MAX_TURNS = 3;
|
|
33
|
+
/** The argv the pass adds to the one-shot argv: no tools, no MCP servers. Pure. */
|
|
34
|
+
export function buildReleaseNoteArgs() {
|
|
35
|
+
return ['--tools', '', '--strict-mcp-config'];
|
|
36
|
+
}
|
|
37
|
+
const FENCED_JSON = /```json[^\S\n]*\n([\s\S]*?)```/gi;
|
|
38
|
+
function isRecord(value) {
|
|
39
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse a release-note answer: the last fenced json block, holding a note
|
|
43
|
+
* object. Only the note's own fields are kept (a title is the release's own,
|
|
44
|
+
* set by the edge). Pure.
|
|
45
|
+
*/
|
|
46
|
+
export function parseReleaseNoteOutput(text) {
|
|
47
|
+
const blocks = [...text.matchAll(FENCED_JSON)].map((m) => m[1] ?? '');
|
|
48
|
+
const last = blocks.at(-1);
|
|
49
|
+
if (last === undefined)
|
|
50
|
+
return { kind: 'error', error: 'the answer has no fenced json block' };
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(last);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return { kind: 'error', error: `the last json block is not valid JSON: ${err.message}` };
|
|
57
|
+
}
|
|
58
|
+
if (!isRecord(parsed))
|
|
59
|
+
return { kind: 'error', error: 'the last json block is not an object' };
|
|
60
|
+
const { summary, items, knownIssues } = parsed;
|
|
61
|
+
if (typeof summary !== 'string' || summary.trim() === '')
|
|
62
|
+
return { kind: 'error', error: 'the note has no summary' };
|
|
63
|
+
if (!Array.isArray(items) || items.length === 0)
|
|
64
|
+
return { kind: 'error', error: 'the note lists no items' };
|
|
65
|
+
const bad = items.findIndex((item) => !isRecord(item));
|
|
66
|
+
if (bad !== -1)
|
|
67
|
+
return { kind: 'error', error: `item ${bad + 1} is not an object` };
|
|
68
|
+
const note = { summary, items: items };
|
|
69
|
+
if (knownIssues !== undefined && knownIssues !== null)
|
|
70
|
+
note.knownIssues = knownIssues;
|
|
71
|
+
return { kind: 'note', note };
|
|
72
|
+
}
|
|
73
|
+
function errorMessage(err) {
|
|
74
|
+
return err instanceof Error ? err.message : String(err);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Author one release's note -- or, given a correction, the whole note again
|
|
78
|
+
* with the refused note and its violations appended. Never throws for a model
|
|
79
|
+
* or parse problem (those are results).
|
|
80
|
+
*/
|
|
81
|
+
export async function authorReleaseNote(context, deps, correction = null) {
|
|
82
|
+
const attempt = correction ? 'corrective attempt' : 'drafting pass';
|
|
83
|
+
let prompt = buildReleaseNotePrompt(context);
|
|
84
|
+
if (correction)
|
|
85
|
+
prompt = `${prompt}\n${buildReleaseNoteCorrection(correction.violations, correction.previous)}`;
|
|
86
|
+
const label = `release-note:${context.releaseId.slice(0, 8)}${correction ? ':corrective' : ''}`;
|
|
87
|
+
let text;
|
|
88
|
+
try {
|
|
89
|
+
text = (await deps.invoke(RELEASE_NOTE_SYSTEM_PROMPT, prompt, {
|
|
90
|
+
label,
|
|
91
|
+
extraArgs: buildReleaseNoteArgs(),
|
|
92
|
+
timeoutMs: RELEASE_NOTE_TIMEOUT_MS,
|
|
93
|
+
maxTurns: RELEASE_NOTE_MAX_TURNS,
|
|
94
|
+
})).text;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (isModelUnavailable(err) && err.kind === 'rate_limit') {
|
|
98
|
+
return { kind: 'held', reason: `the ${attempt} hit the provider rate limit: ${err.message}` };
|
|
99
|
+
}
|
|
100
|
+
return { kind: 'failed', reason: `the ${attempt} could not run: ${errorMessage(err)}` };
|
|
101
|
+
}
|
|
102
|
+
const out = parseReleaseNoteOutput(text);
|
|
103
|
+
if (out.kind === 'error')
|
|
104
|
+
return { kind: 'failed', reason: `the ${attempt} answered no usable note: ${out.error}` };
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=release-note-author.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-note-author.js","sourceRoot":"","sources":["../../src/release-notes/release-note-author.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DraftingContext, ReleaseNoteDraft } from '../queries/release-notes/release-notes.js';
|
|
2
|
+
import { type ReleaseNoteAuthorDeps } from './release-note-author.js';
|
|
3
|
+
/** The check's wall-clock bound. */
|
|
4
|
+
export declare const RELEASE_NOTE_FAITHFULNESS_TIMEOUT_MS: number;
|
|
5
|
+
/** Its turn cap: it has no tools, so it answers in one. */
|
|
6
|
+
export declare const RELEASE_NOTE_FAITHFULNESS_MAX_TURNS = 2;
|
|
7
|
+
export type ReleaseNoteJudgement = {
|
|
8
|
+
kind: 'faithful';
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'unfaithful';
|
|
11
|
+
violations: string[];
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'failed';
|
|
14
|
+
reason: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'held';
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
export interface FaithfulnessVerdict {
|
|
20
|
+
item: number;
|
|
21
|
+
faithful: boolean;
|
|
22
|
+
reason: string;
|
|
23
|
+
}
|
|
24
|
+
export type FaithfulnessOutput = {
|
|
25
|
+
kind: 'verdicts';
|
|
26
|
+
verdicts: FaithfulnessVerdict[];
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'error';
|
|
29
|
+
error: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Read the check's answer: the last fenced json block, holding exactly one
|
|
33
|
+
* verdict for each of the note's `itemCount` items. Pure.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseFaithfulnessOutput(text: string, itemCount: number): FaithfulnessOutput;
|
|
36
|
+
/** A not-faithful verdict as a violation the corrective attempt is shown. */
|
|
37
|
+
export declare function faithfulnessViolation(note: ReleaseNoteDraft, verdict: FaithfulnessVerdict): string;
|
|
38
|
+
/** Judge every item's Why against its work's source. Never throws for a model or parse problem. */
|
|
39
|
+
export declare function judgeReleaseNoteWhys(context: DraftingContext, note: ReleaseNoteDraft, deps: ReleaseNoteAuthorDeps): Promise<ReleaseNoteJudgement>;
|
|
40
|
+
//# sourceMappingURL=release-note-faithfulness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-note-faithfulness.d.ts","sourceRoot":"","sources":["../../src/release-notes/release-note-faithfulness.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAKnG,OAAO,EAAwB,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAE5F,oCAAoC;AACpC,eAAO,MAAM,oCAAoC,QAAa,CAAC;AAC/D,2DAA2D;AAC3D,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAErD,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,mBAAmB,EAAE,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAQ1H;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,kBAAkB,CAwB3F;AAOD,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAIlG;AAMD,mGAAmG;AACnG,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/release-notes/release-note-faithfulness.test.ts,
|
|
3
|
+
// packages/daemon/src/release-notes/release-note-pass.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
/**
|
|
6
|
+
* The drafting pass's MODEL faithfulness check: before a drafted note is
|
|
7
|
+
* written, a FRESH one-shot model run (no tools, no MCP servers) reads each
|
|
8
|
+
* item's Why against the source of the work it names -- the requests users
|
|
9
|
+
* raised, else the problem the work states -- and answers, per item, whether
|
|
10
|
+
* the Why states it (prompt-sections/release-note-faithfulness-section.ts).
|
|
11
|
+
*
|
|
12
|
+
* This is where a Why's MEANING is judged. The edge refuses only a Why that
|
|
13
|
+
* turns its source around by adding or dropping a negation; whether it states
|
|
14
|
+
* its source well is this check's and, after it, the human's who edits and
|
|
15
|
+
* publishes the note (docs/DECISIONS.md, "A release carries done deliveries
|
|
16
|
+
* once per environment", item 7).
|
|
17
|
+
*
|
|
18
|
+
* An item judged unfaithful comes back to the pass as a violation, on the same
|
|
19
|
+
* footing as an edge refusal: the pass re-asks the author ONCE with it
|
|
20
|
+
* appended (release-note-pass.ts). A provider rate limit is `held` (the claim
|
|
21
|
+
* is given back); a run that fails, or an answer that is not one verdict per
|
|
22
|
+
* item, is `failed` -- a note the check could not judge is not written as if
|
|
23
|
+
* it had been. The model run is injected, so no test needs a claude binary.
|
|
24
|
+
*/
|
|
25
|
+
import { isModelUnavailable } from '../backends/claude/one-shot-model.js';
|
|
26
|
+
import { buildReleaseNoteFaithfulnessPrompt, RELEASE_NOTE_FAITHFULNESS_SYSTEM_PROMPT, } from '../prompt-sections/release-note-faithfulness-section.js';
|
|
27
|
+
import { buildReleaseNoteArgs } from './release-note-author.js';
|
|
28
|
+
/** The check's wall-clock bound. */
|
|
29
|
+
export const RELEASE_NOTE_FAITHFULNESS_TIMEOUT_MS = 5 * 60_000;
|
|
30
|
+
/** Its turn cap: it has no tools, so it answers in one. */
|
|
31
|
+
export const RELEASE_NOTE_FAITHFULNESS_MAX_TURNS = 2;
|
|
32
|
+
const FENCED_JSON = /```json[^\S\n]*\n([\s\S]*?)```/gi;
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read the check's answer: the last fenced json block, holding exactly one
|
|
38
|
+
* verdict for each of the note's `itemCount` items. Pure.
|
|
39
|
+
*/
|
|
40
|
+
export function parseFaithfulnessOutput(text, itemCount) {
|
|
41
|
+
const last = [...text.matchAll(FENCED_JSON)].map((m) => m[1] ?? '').at(-1);
|
|
42
|
+
if (last === undefined)
|
|
43
|
+
return { kind: 'error', error: 'the answer has no fenced json block' };
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(last);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return { kind: 'error', error: `the last json block is not valid JSON: ${err.message}` };
|
|
50
|
+
}
|
|
51
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.items))
|
|
52
|
+
return { kind: 'error', error: 'the answer lists no items' };
|
|
53
|
+
const byItem = new Map();
|
|
54
|
+
for (const entry of parsed.items) {
|
|
55
|
+
if (!isRecord(entry) || typeof entry.item !== 'number' || typeof entry.faithful !== 'boolean') {
|
|
56
|
+
return { kind: 'error', error: 'a verdict is not {item, faithful, reason}' };
|
|
57
|
+
}
|
|
58
|
+
byItem.set(entry.item, { item: entry.item, faithful: entry.faithful, reason: typeof entry.reason === 'string' ? entry.reason.trim() : '' });
|
|
59
|
+
}
|
|
60
|
+
const verdicts = [];
|
|
61
|
+
for (let item = 1; item <= itemCount; item++) {
|
|
62
|
+
const verdict = byItem.get(item);
|
|
63
|
+
if (!verdict)
|
|
64
|
+
return { kind: 'error', error: `item ${item} has no verdict` };
|
|
65
|
+
verdicts.push(verdict);
|
|
66
|
+
}
|
|
67
|
+
return { kind: 'verdicts', verdicts };
|
|
68
|
+
}
|
|
69
|
+
function headlineOf(note, index) {
|
|
70
|
+
const headline = note.items[index]?.headline;
|
|
71
|
+
return typeof headline === 'string' ? headline.slice(0, 60) : '';
|
|
72
|
+
}
|
|
73
|
+
/** A not-faithful verdict as a violation the corrective attempt is shown. */
|
|
74
|
+
export function faithfulnessViolation(note, verdict) {
|
|
75
|
+
return `item ${verdict.item} ("${headlineOf(note, verdict.item - 1)}"): its Why does not state its work's source -- ` +
|
|
76
|
+
`${verdict.reason || 'the check gave no reason'}: say the request users raised (or the problem the work states), ` +
|
|
77
|
+
'in its sense, in the reader\'s words';
|
|
78
|
+
}
|
|
79
|
+
function errorMessage(err) {
|
|
80
|
+
return err instanceof Error ? err.message : String(err);
|
|
81
|
+
}
|
|
82
|
+
/** Judge every item's Why against its work's source. Never throws for a model or parse problem. */
|
|
83
|
+
export async function judgeReleaseNoteWhys(context, note, deps) {
|
|
84
|
+
let text;
|
|
85
|
+
try {
|
|
86
|
+
text = (await deps.invoke(RELEASE_NOTE_FAITHFULNESS_SYSTEM_PROMPT, buildReleaseNoteFaithfulnessPrompt(context, note), {
|
|
87
|
+
label: `release-note:${context.releaseId.slice(0, 8)}:faithfulness`,
|
|
88
|
+
extraArgs: buildReleaseNoteArgs(),
|
|
89
|
+
timeoutMs: RELEASE_NOTE_FAITHFULNESS_TIMEOUT_MS,
|
|
90
|
+
maxTurns: RELEASE_NOTE_FAITHFULNESS_MAX_TURNS,
|
|
91
|
+
})).text;
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (isModelUnavailable(err) && err.kind === 'rate_limit') {
|
|
95
|
+
return { kind: 'held', reason: `the faithfulness check hit the provider rate limit: ${err.message}` };
|
|
96
|
+
}
|
|
97
|
+
return { kind: 'failed', reason: `the faithfulness check could not run: ${errorMessage(err)}` };
|
|
98
|
+
}
|
|
99
|
+
const out = parseFaithfulnessOutput(text, note.items.length);
|
|
100
|
+
if (out.kind === 'error')
|
|
101
|
+
return { kind: 'failed', reason: `the faithfulness check answered no usable verdicts: ${out.error}` };
|
|
102
|
+
const unfaithful = out.verdicts.filter((v) => !v.faithful);
|
|
103
|
+
return unfaithful.length === 0
|
|
104
|
+
? { kind: 'faithful' }
|
|
105
|
+
: { kind: 'unfaithful', violations: unfaithful.map((v) => faithfulnessViolation(note, v)) };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=release-note-faithfulness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-note-faithfulness.js","sourceRoot":"","sources":["../../src/release-notes/release-note-faithfulness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release-note drafting pass: for every configured product, the releases a
|
|
3
|
+
* human has cut that are still drafting and held by no live pass get their
|
|
4
|
+
* note drafted, one release at a time.
|
|
5
|
+
*
|
|
6
|
+
* Per candidate: claim it (a release another pass holds, or that stopped
|
|
7
|
+
* drafting, is skipped) -> author the note from the drafting context the claim
|
|
8
|
+
* answered (release-note-author.ts) -> JUDGE it: the model faithfulness check
|
|
9
|
+
* reads each item's Why against its work's source (release-note-faithfulness.ts)
|
|
10
|
+
* -> write it as the release's draft under the claim. The edge stores a draft
|
|
11
|
+
* only when it is a note in the one format and keeps its rules. A draft the
|
|
12
|
+
* check finds unfaithful, or the edge REFUSES, is authored ONCE more with what
|
|
13
|
+
* was wrong appended, judged and written again; a second rejection is written
|
|
14
|
+
* as the release's failure with what was said (a human redraft retries). A
|
|
15
|
+
* check that cannot judge the note records the failure too: a note is never
|
|
16
|
+
* written as if it had been judged.
|
|
17
|
+
* A draft written after the human changed the release's work is a conflict:
|
|
18
|
+
* the claim is given back, so the next tick drafts the release again from the
|
|
19
|
+
* work it now carries. A claim this pass lost is left alone. A provider rate
|
|
20
|
+
* limit gives the claim back too: the next tick may take it at once.
|
|
21
|
+
*
|
|
22
|
+
* The pass's only writes are those release-note writes (product-api
|
|
23
|
+
* daemon_release_note_claim / daemon_release_note_write); it never writes a
|
|
24
|
+
* focus, a delivery or an arm.
|
|
25
|
+
*
|
|
26
|
+
* IT NEVER DELAYS ARMED WORK. It acquires no ResourceGovernor slot and never
|
|
27
|
+
* waits on one: its hold (composition/release-note-pass-context.ts) reads the
|
|
28
|
+
* governor's utilization and does not start, or go on to the next release,
|
|
29
|
+
* while every session slot is taken or a spawn is queued for one, while the
|
|
30
|
+
* claude provider's rate-limit window is open, or while the daemon is
|
|
31
|
+
* read-only. At most ONE pass runs per daemon: a tick that finds one in flight
|
|
32
|
+
* returns at once.
|
|
33
|
+
*/
|
|
34
|
+
import type { ResourceGovernor } from '@telora/daemon-core';
|
|
35
|
+
import { type DraftingContext, type ReleaseNoteCandidate, type ReleaseNoteClaim, type ReleaseNoteDraft, type ReleaseNoteWrite } from '../queries/release-notes/release-notes.js';
|
|
36
|
+
import type { ReleaseNoteAuthorResult, ReleaseNoteCorrection } from './release-note-author.js';
|
|
37
|
+
import type { ReleaseNoteJudgement } from './release-note-faithfulness.js';
|
|
38
|
+
/** The loop's cadence. */
|
|
39
|
+
export declare const RELEASE_NOTE_PASS_INTERVAL_MS = 60000;
|
|
40
|
+
export interface ReleaseNotePassDeps {
|
|
41
|
+
products: ReadonlyArray<{
|
|
42
|
+
id: string;
|
|
43
|
+
}>;
|
|
44
|
+
readCandidates: (productId: string) => Promise<ReleaseNoteCandidate[]>;
|
|
45
|
+
claim: (releaseId: string) => Promise<ReleaseNoteClaim>;
|
|
46
|
+
write: (write: ReleaseNoteWrite) => Promise<void>;
|
|
47
|
+
/** Give the claim back; false when this pass no longer held it. */
|
|
48
|
+
unclaim: (releaseId: string, claimId: string) => Promise<boolean>;
|
|
49
|
+
author: (context: DraftingContext, correction: ReleaseNoteCorrection | null) => Promise<ReleaseNoteAuthorResult>;
|
|
50
|
+
/** The model faithfulness check: does each item's Why state its work's source? */
|
|
51
|
+
judge: (context: DraftingContext, note: ReleaseNoteDraft) => Promise<ReleaseNoteJudgement>;
|
|
52
|
+
/** Why the pass must not start or go on to the next release now, or null. */
|
|
53
|
+
holdReason: () => string | null;
|
|
54
|
+
}
|
|
55
|
+
/** The pass's deps over the running daemon, or null before it has a live config (composition/release-note-pass-context.ts). */
|
|
56
|
+
export type ReleaseNotePassDepsSource = (governor: ResourceGovernor | null) => ReleaseNotePassDeps | null;
|
|
57
|
+
export interface ReleaseNotePassOutcome {
|
|
58
|
+
/** Why the pass stopped early or did not start, when it did. */
|
|
59
|
+
skipped: string | null;
|
|
60
|
+
/** Drafts stored. */
|
|
61
|
+
drafted: number;
|
|
62
|
+
/** Releases written draft_failed. */
|
|
63
|
+
failed: number;
|
|
64
|
+
/** Claims given back (a provider rate limit, or the release's work changed while drafting). */
|
|
65
|
+
released: number;
|
|
66
|
+
/** Claims this pass lost to another (or the release left drafting) before it wrote. */
|
|
67
|
+
lost: number;
|
|
68
|
+
/** Candidates another pass held when this one tried to claim them. */
|
|
69
|
+
notClaimed: number;
|
|
70
|
+
/** Failed reads, claims and writes. */
|
|
71
|
+
errors: number;
|
|
72
|
+
}
|
|
73
|
+
/** Run one pass over every product. One product's failed read never stops another's. */
|
|
74
|
+
export declare function runReleaseNotePass(deps: ReleaseNotePassDeps): Promise<ReleaseNotePassOutcome>;
|
|
75
|
+
/** The loop tick: one pass over the live deps, never throwing into the timer. */
|
|
76
|
+
export declare function runReleaseNotePassSafely(source: ReleaseNotePassDepsSource, governor: ResourceGovernor | null): Promise<void>;
|
|
77
|
+
//# sourceMappingURL=release-note-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-note-pass.d.ts","sourceRoot":"","sources":["../../src/release-notes/release-note-pass.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAEtB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAI3E,0BAA0B;AAC1B,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACvE,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxD,KAAK,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,mEAAmE;IACnE,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,MAAM,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,qBAAqB,GAAG,IAAI,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACjH,kFAAkF;IAClF,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC3F,6EAA6E;IAC7E,UAAU,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,+HAA+H;AAC/H,MAAM,MAAM,yBAAyB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,KAAK,mBAAmB,GAAG,IAAI,CAAC;AAE1G,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;CAChB;AA2JD,wFAAwF;AACxF,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA0BnG;AAED,iFAAiF;AACjF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,yBAAyB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAclI"}
|