@deftai/directive-core 0.97.0 → 0.98.0
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/authz/classify.js +291 -0
- package/dist/check/cached-orchestrator.d.ts +5 -0
- package/dist/check/cached-orchestrator.js +18 -1
- package/dist/check/gate-lists.d.ts +20 -0
- package/dist/check/gate-lists.js +46 -9
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +1 -1
- package/dist/check/orchestrator.d.ts +4 -0
- package/dist/check/orchestrator.js +4 -0
- package/dist/doctor/checks.d.ts +7 -0
- package/dist/doctor/checks.js +83 -0
- package/dist/hooks/dispatcher.d.ts +5 -0
- package/dist/hooks/dispatcher.js +54 -4
- package/dist/init-deposit/hygiene.d.ts +70 -1
- package/dist/init-deposit/hygiene.js +582 -8
- package/dist/init-deposit/scaffold.js +277 -4
- package/dist/init-deposit/skill-discovery-deposit.js +15 -0
- package/dist/policy/check-resume.d.ts +72 -0
- package/dist/policy/check-resume.js +253 -0
- package/dist/policy/coverage-check-resume-presets.d.ts +46 -0
- package/dist/policy/coverage-check-resume-presets.js +228 -0
- package/dist/policy/coverage-debt.d.ts +76 -0
- package/dist/policy/coverage-debt.js +262 -0
- package/dist/policy/index.d.ts +3 -0
- package/dist/policy/index.js +50 -21
- package/dist/release/auto-hatch.d.ts +114 -0
- package/dist/release/auto-hatch.js +301 -0
- package/dist/release/coverage-debt-ledger.d.ts +22 -0
- package/dist/release/coverage-debt-ledger.js +157 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/pipeline.js +164 -12
- package/dist/release/suite-stamp.d.ts +44 -0
- package/dist/release/suite-stamp.js +133 -0
- package/dist/release/types.d.ts +19 -0
- package/dist/session/coverage-check-resume-nudge.d.ts +34 -0
- package/dist/session/coverage-check-resume-nudge.js +66 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/session-start.js +21 -0
- package/dist/triage/classify/label-mirror.d.ts +31 -1
- package/dist/triage/classify/label-mirror.js +78 -6
- package/dist/triage/help/registry-data.d.ts +6 -6
- package/dist/triage/help/registry-data.js +12 -3
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +54 -0
- package/package.json +3 -3
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-start skippable nudge for coverageDebt + checkResume (#3189).
|
|
3
|
+
*
|
|
4
|
+
* Why / what + Strict / Hatch-aware / Later (+ Discuss / Back for #1470).
|
|
5
|
+
* Later does not mark decided; re-nag next interactive mutation ritual.
|
|
6
|
+
* Headless / CI / non-TTY fail-open (no block).
|
|
7
|
+
*/
|
|
8
|
+
import { isCoverageCheckResumeUndecided } from "../policy/coverage-check-resume-presets.js";
|
|
9
|
+
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
10
|
+
import { isHeadlessSession } from "../product-signal/headless.js";
|
|
11
|
+
/** Why block -- must appear in the nudge. */
|
|
12
|
+
export const COVERAGE_CHECK_RESUME_NUDGE_WHY = "Why: Long checks often fail late on small gates, or barely miss coverage. " +
|
|
13
|
+
"The project can fail closed, warn, or hatch with a tracked debt issue on THIS repo. " +
|
|
14
|
+
"Local machines may resume a suite that already passed at the same commit; " +
|
|
15
|
+
"CI must not trust a laptop stamp.";
|
|
16
|
+
/** What block -- bundled presets, not five micro-toggles. */
|
|
17
|
+
export const COVERAGE_CHECK_RESUME_NUDGE_WHAT = "What we need: one bundled project decision (not USER.md personal prefs; not npm publish; " +
|
|
18
|
+
"not turning off required CI):\n" +
|
|
19
|
+
" * Strict (recommended for most apps) -- coverageDebt.mode=off, checkResume.localStamp=off\n" +
|
|
20
|
+
" * Hatch-aware -- coverageDebt.mode=hatch (autoFile=false by default), localStamp=on for DX\n" +
|
|
21
|
+
" * Later -- skip this session; does NOT set status=decided; nag again next ritual\n" +
|
|
22
|
+
" * Discuss -- talk through the trade-offs\n" +
|
|
23
|
+
" * Back -- leave this prompt without choosing\n" +
|
|
24
|
+
"Apply path (writes PROJECT-DEFINITION):\n" +
|
|
25
|
+
" Strict -> `" +
|
|
26
|
+
policyColonInvocation("coverage-check-resume-preset", " -- --preset strict") +
|
|
27
|
+
"`\n" +
|
|
28
|
+
" Hatch-aware -> `" +
|
|
29
|
+
policyColonInvocation("coverage-check-resume-preset", " -- --preset hatch-aware") +
|
|
30
|
+
"`\n" +
|
|
31
|
+
" Later -> `" +
|
|
32
|
+
policyColonInvocation("coverage-check-resume-later") +
|
|
33
|
+
"` (no PD write)\n" +
|
|
34
|
+
" Dismiss-with-reason -> `" +
|
|
35
|
+
policyColonInvocation("coverage-check-resume-dismiss", ' -- --reason "…"') +
|
|
36
|
+
"`\n" +
|
|
37
|
+
"Stop nag only after Strict / Hatch-aware (status=decided) or dismiss-with-reason " +
|
|
38
|
+
`(visible via \`${policyColonInvocation("show", " --field=coverageDebt")}\` / doctor).`;
|
|
39
|
+
export const COVERAGE_CHECK_RESUME_NUDGE_BODY = `${COVERAGE_CHECK_RESUME_NUDGE_WHY}\n\n${COVERAGE_CHECK_RESUME_NUDGE_WHAT}`;
|
|
40
|
+
/**
|
|
41
|
+
* True when an interactive mutation session-start ritual should surface the
|
|
42
|
+
* coverage/check-resume decision nudge (#3189).
|
|
43
|
+
*/
|
|
44
|
+
export function isCoverageCheckResumeNudgeEligible(options) {
|
|
45
|
+
if (isHeadlessSession(options)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return isCoverageCheckResumeUndecided(options.projectRoot);
|
|
49
|
+
}
|
|
50
|
+
/** Format the full operator-facing nudge. */
|
|
51
|
+
export function formatCoverageCheckResumeNudge() {
|
|
52
|
+
return `[deft policy] coverageDebt + checkResume undecided:\n${COVERAGE_CHECK_RESUME_NUDGE_BODY}\n`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Emit the nudge when eligible; headless / decided callers get an empty string.
|
|
56
|
+
* Never blocks -- session-start always continues.
|
|
57
|
+
*
|
|
58
|
+
* Design note (#3189): session-start is non-interactive for agent hosts (same
|
|
59
|
+
* class as product-signal D17). Choice dispatch is via CLI verbs named in the
|
|
60
|
+
* nudge body (`policy:coverage-check-resume-preset|later|dismiss`), not a
|
|
61
|
+
* blocking TTY menu inside session:start.
|
|
62
|
+
*/
|
|
63
|
+
export function maybeFormatCoverageCheckResumeNudge(options) {
|
|
64
|
+
return isCoverageCheckResumeNudgeEligible(options) ? formatCoverageCheckResumeNudge() : "";
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=coverage-check-resume-nudge.js.map
|
package/dist/session/index.d.ts
CHANGED
package/dist/session/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { runDefaultMode } from "../triage/welcome/default-mode.js";
|
|
|
18
18
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
19
19
|
import { emitSessionValueReadback } from "../value/readback.js";
|
|
20
20
|
import { verifyRequiredTools } from "../verify-env/verify-tools.js";
|
|
21
|
+
import { maybeFormatCoverageCheckResumeNudge } from "./coverage-check-resume-nudge.js";
|
|
21
22
|
import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
|
|
22
23
|
import { emitSessionStartProcessCost } from "./process-cost.js";
|
|
23
24
|
import { probeSessionReleaseAvailability, } from "./release-availability.js";
|
|
@@ -433,6 +434,16 @@ function runSessionRearm(projectRoot, options, instant, environment) {
|
|
|
433
434
|
if (humanMergeLine !== null) {
|
|
434
435
|
lines.push(humanMergeLine);
|
|
435
436
|
}
|
|
437
|
+
// #3189: re-arm still surfaces undecided coverage/check-resume once per ritual.
|
|
438
|
+
try {
|
|
439
|
+
const coverageNudge = maybeFormatCoverageCheckResumeNudge({ projectRoot });
|
|
440
|
+
if (coverageNudge.length > 0) {
|
|
441
|
+
lines.push(coverageNudge.trimEnd());
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// best-effort — re-arm must not abort
|
|
446
|
+
}
|
|
436
447
|
const priorQuick = eligibility.state.quickSteps;
|
|
437
448
|
const priorTriage = priorQuick.triage_welcome ?? ritualStep({ ok: true, ts: instant });
|
|
438
449
|
const policyOk = policyResult.error === null || policyResult.source === "default-fail-closed";
|
|
@@ -863,6 +874,16 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
863
874
|
if (consentPrompt.length > 0) {
|
|
864
875
|
lines.push(consentPrompt.trimEnd());
|
|
865
876
|
}
|
|
877
|
+
// #3189: skippable coverageDebt/checkResume project-decision nudge (fail-open; never blocks).
|
|
878
|
+
try {
|
|
879
|
+
const coverageNudge = maybeFormatCoverageCheckResumeNudge({ projectRoot });
|
|
880
|
+
if (coverageNudge.length > 0) {
|
|
881
|
+
lines.push(coverageNudge.trimEnd());
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
catch {
|
|
885
|
+
// best-effort operator advisory — session start must not abort
|
|
886
|
+
}
|
|
866
887
|
const writeStarted = performance.now();
|
|
867
888
|
const coldSessionId = (options.newSessionId ?? randomUUID)();
|
|
868
889
|
const payload = newRitualStatePayload({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tier-1 deterministic SCM label mirror (#1423 Wave 1 + Wave 2 bootstrap).
|
|
2
|
+
* Tier-1 deterministic SCM label mirror (#1423 Wave 1 + Wave 2 bootstrap + #3197 re-enrich).
|
|
3
3
|
*
|
|
4
4
|
* Classifies cached issues with the existing #1129 engine, then mirrors the
|
|
5
5
|
* outcome as SCM labels (dry-run default, --apply to write). Never accepts into
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* + samples), batched rate-limit-aware apply. Bootstrap mass-triage entrypoint is
|
|
10
10
|
* `triage:classify -- --mirror` with these filters (not triage:accept).
|
|
11
11
|
*
|
|
12
|
+
* #3197 re-enrich: default keeps one-shot skip on idempotencyLabel; opt-in
|
|
13
|
+
* `--re-enrich` re-classifies already-stamped issues and plans **additive**
|
|
14
|
+
* label deltas only (v1; no removals / no full reconcile).
|
|
15
|
+
*
|
|
12
16
|
* Intentionally does NOT import from ./index.js (SLizard P1 cycle). The classify
|
|
13
17
|
* engine is injected via LabelMirrorEngine / mirrorLabels() wrapper in index.ts.
|
|
14
18
|
*/
|
|
@@ -98,6 +102,12 @@ export interface LabelMirrorItem {
|
|
|
98
102
|
readonly add: readonly string[];
|
|
99
103
|
readonly status: LabelMirrorStatus;
|
|
100
104
|
readonly message?: string;
|
|
105
|
+
/**
|
|
106
|
+
* True when this row re-planned an issue that already carried the idempotency
|
|
107
|
+
* label under opt-in re-enrich mode (#3197). Distinguishes first-time stamp
|
|
108
|
+
* rows from re-enrich additive backfill in dry-run digests.
|
|
109
|
+
*/
|
|
110
|
+
readonly re_enrich?: boolean;
|
|
101
111
|
}
|
|
102
112
|
/** Operator digest aggregates for bootstrap mass-triage (#3125 / #1423 Wave 2). */
|
|
103
113
|
export interface LabelMirrorDigest {
|
|
@@ -116,6 +126,8 @@ export interface LabelMirrorFilters {
|
|
|
116
126
|
readonly author: string | null;
|
|
117
127
|
/** Resolved author logins for machine consumers. */
|
|
118
128
|
readonly author_logins: readonly string[] | null;
|
|
129
|
+
/** Whether this run used opt-in re-enrich mode (#3197). */
|
|
130
|
+
readonly re_enrich: boolean;
|
|
119
131
|
}
|
|
120
132
|
export interface LabelMirrorOutcome {
|
|
121
133
|
readonly project_root: string;
|
|
@@ -132,6 +144,16 @@ export interface LabelMirrorOutcome {
|
|
|
132
144
|
/** Issues skipped by --author filter (#3129). */
|
|
133
145
|
readonly skipped_author: number;
|
|
134
146
|
readonly errors: number;
|
|
147
|
+
/**
|
|
148
|
+
* Planned rows that re-enriched already-stamped issues (subset of planned; #3197).
|
|
149
|
+
* Zero when re_enrich mode is off.
|
|
150
|
+
*/
|
|
151
|
+
readonly re_enrich_planned: number;
|
|
152
|
+
/**
|
|
153
|
+
* Applied rows that re-enriched already-stamped issues (subset of applied; #3197).
|
|
154
|
+
* Zero when re_enrich mode is off.
|
|
155
|
+
*/
|
|
156
|
+
readonly re_enrich_applied: number;
|
|
135
157
|
readonly filters: LabelMirrorFilters;
|
|
136
158
|
readonly digest: LabelMirrorDigest;
|
|
137
159
|
readonly items: readonly LabelMirrorItem[];
|
|
@@ -169,6 +191,14 @@ export interface LabelMirrorOptions {
|
|
|
169
191
|
readonly delayMs?: number;
|
|
170
192
|
/** Injectable sleep for tests (receives ms). Default busy-wait when delayMs > 0. */
|
|
171
193
|
readonly sleepMs?: LabelMirrorSleepFn;
|
|
194
|
+
/**
|
|
195
|
+
* Opt-in re-enrich mode (#3197): re-classify issues that already carry the
|
|
196
|
+
* idempotency label and plan **additive** label deltas only (no removals).
|
|
197
|
+
* Default false preserves one-shot `skipped_already_triaged` behavior.
|
|
198
|
+
* Still dry-run by default; pair with dryRun:false / CLI `--apply` to write.
|
|
199
|
+
* Never triage:accept / never xBRIEF writes.
|
|
200
|
+
*/
|
|
201
|
+
readonly reEnrich?: boolean;
|
|
172
202
|
/** Required: classify engine (provided by classify/index mirrorLabels wrapper). */
|
|
173
203
|
readonly engine: LabelMirrorEngine;
|
|
174
204
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tier-1 deterministic SCM label mirror (#1423 Wave 1 + Wave 2 bootstrap).
|
|
2
|
+
* Tier-1 deterministic SCM label mirror (#1423 Wave 1 + Wave 2 bootstrap + #3197 re-enrich).
|
|
3
3
|
*
|
|
4
4
|
* Classifies cached issues with the existing #1129 engine, then mirrors the
|
|
5
5
|
* outcome as SCM labels (dry-run default, --apply to write). Never accepts into
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* + samples), batched rate-limit-aware apply. Bootstrap mass-triage entrypoint is
|
|
10
10
|
* `triage:classify -- --mirror` with these filters (not triage:accept).
|
|
11
11
|
*
|
|
12
|
+
* #3197 re-enrich: default keeps one-shot skip on idempotencyLabel; opt-in
|
|
13
|
+
* `--re-enrich` re-classifies already-stamped issues and plans **additive**
|
|
14
|
+
* label deltas only (v1; no removals / no full reconcile).
|
|
15
|
+
*
|
|
12
16
|
* Intentionally does NOT import from ./index.js (SLizard P1 cycle). The classify
|
|
13
17
|
* engine is injected via LabelMirrorEngine / mirrorLabels() wrapper in index.ts.
|
|
14
18
|
*/
|
|
@@ -399,6 +403,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
399
403
|
? options.authorFilter
|
|
400
404
|
: null;
|
|
401
405
|
const sampleLimit = options.sampleLimit ?? DEFAULT_DIGEST_SAMPLE_LIMIT;
|
|
406
|
+
const reEnrich = options.reEnrich === true;
|
|
402
407
|
const batchSize = options.batchSize !== undefined
|
|
403
408
|
? Math.max(1, Math.floor(options.batchSize))
|
|
404
409
|
: DEFAULT_APPLY_BATCH_SIZE;
|
|
@@ -416,6 +421,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
416
421
|
repo: repoFilter,
|
|
417
422
|
author: authorFilter !== null ? authorFilter.display : null,
|
|
418
423
|
author_logins: authorFilter !== null ? authorFilter.allowLogins : null,
|
|
424
|
+
re_enrich: reEnrich,
|
|
419
425
|
};
|
|
420
426
|
const items = [];
|
|
421
427
|
const outcomeBase = {
|
|
@@ -441,6 +447,8 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
441
447
|
skipped_unreadable: 0,
|
|
442
448
|
skipped_closed: 0,
|
|
443
449
|
skipped_author: 0,
|
|
450
|
+
re_enrich_planned: 0,
|
|
451
|
+
re_enrich_applied: 0,
|
|
444
452
|
errors: 0,
|
|
445
453
|
digest: emptyDigest,
|
|
446
454
|
items: [
|
|
@@ -481,6 +489,8 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
481
489
|
let skippedUnreadable = 0;
|
|
482
490
|
let skippedClosed = 0;
|
|
483
491
|
let skippedAuthor = 0;
|
|
492
|
+
let reEnrichPlanned = 0;
|
|
493
|
+
let reEnrichApplied = 0;
|
|
484
494
|
let errors = 0;
|
|
485
495
|
let applyWritesSinceSleep = 0;
|
|
486
496
|
for (const [repo, issueNumber] of pairs) {
|
|
@@ -573,7 +583,10 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
573
583
|
else {
|
|
574
584
|
current = issueLabelNames(issue);
|
|
575
585
|
}
|
|
576
|
-
|
|
586
|
+
// Default one-shot: already stamped → skip. Opt-in --re-enrich continues past
|
|
587
|
+
// the stamp and plans additive deltas only (#3197; never removals in v1).
|
|
588
|
+
const alreadyStamped = policy.idempotencyLabel.length > 0 && current.includes(policy.idempotencyLabel);
|
|
589
|
+
if (alreadyStamped && !reEnrich) {
|
|
577
590
|
skippedAlready += 1;
|
|
578
591
|
items.push({
|
|
579
592
|
repo,
|
|
@@ -590,6 +603,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
590
603
|
});
|
|
591
604
|
continue;
|
|
592
605
|
}
|
|
606
|
+
const isReEnrichRow = alreadyStamped && reEnrich;
|
|
593
607
|
// Pass number set only when this exact repo+number is referenced from xBRIEF.
|
|
594
608
|
const key = `${repo.toLowerCase()}\0${issueNumber}`;
|
|
595
609
|
const vbriefReferenced = referencedKeys.has(key)
|
|
@@ -603,6 +617,26 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
603
617
|
now,
|
|
604
618
|
});
|
|
605
619
|
if (classification === null) {
|
|
620
|
+
// Re-enrich of a stamped issue with no current rule match: leave labels as-is
|
|
621
|
+
// (additive-only v1 never strips triaged / action chips). Count as unchanged.
|
|
622
|
+
if (isReEnrichRow) {
|
|
623
|
+
unchanged += 1;
|
|
624
|
+
items.push({
|
|
625
|
+
repo,
|
|
626
|
+
issue_number: issueNumber,
|
|
627
|
+
state,
|
|
628
|
+
action: null,
|
|
629
|
+
reason: null,
|
|
630
|
+
ruleKind: null,
|
|
631
|
+
current: [...current].sort(),
|
|
632
|
+
desired: [...current].sort(),
|
|
633
|
+
add: [],
|
|
634
|
+
status: "unchanged",
|
|
635
|
+
re_enrich: true,
|
|
636
|
+
message: `re-enrich: no classify match; left existing labels (incl. ${policy.idempotencyLabel})`,
|
|
637
|
+
});
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
606
640
|
skippedNoMatch += 1;
|
|
607
641
|
items.push({
|
|
608
642
|
repo,
|
|
@@ -620,7 +654,9 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
620
654
|
}
|
|
621
655
|
const desired = desiredLabelsForClassification(classification.action, policy);
|
|
622
656
|
const currentSet = new Set(current);
|
|
657
|
+
// Additive-only: never plan removals even under re-enrich (#3197 v1).
|
|
623
658
|
const add = desired.filter((label) => !currentSet.has(label)).sort();
|
|
659
|
+
const reEnrichFlag = isReEnrichRow ? { re_enrich: true } : {};
|
|
624
660
|
if (add.length === 0) {
|
|
625
661
|
unchanged += 1;
|
|
626
662
|
items.push({
|
|
@@ -634,11 +670,15 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
634
670
|
desired,
|
|
635
671
|
add: [],
|
|
636
672
|
status: "unchanged",
|
|
673
|
+
...reEnrichFlag,
|
|
637
674
|
});
|
|
638
675
|
continue;
|
|
639
676
|
}
|
|
640
677
|
if (dryRun) {
|
|
641
678
|
planned += 1;
|
|
679
|
+
if (isReEnrichRow) {
|
|
680
|
+
reEnrichPlanned += 1;
|
|
681
|
+
}
|
|
642
682
|
items.push({
|
|
643
683
|
repo,
|
|
644
684
|
issue_number: issueNumber,
|
|
@@ -650,6 +690,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
650
690
|
desired,
|
|
651
691
|
add,
|
|
652
692
|
status: "planned",
|
|
693
|
+
...reEnrichFlag,
|
|
653
694
|
});
|
|
654
695
|
continue;
|
|
655
696
|
}
|
|
@@ -673,6 +714,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
673
714
|
add,
|
|
674
715
|
status: "error",
|
|
675
716
|
message: mutateGate.reason ?? `refusing cross-repo mutation on ${repo}`,
|
|
717
|
+
...reEnrichFlag,
|
|
676
718
|
});
|
|
677
719
|
continue;
|
|
678
720
|
}
|
|
@@ -690,6 +732,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
690
732
|
add,
|
|
691
733
|
status: "error",
|
|
692
734
|
message: "no LabelClient available for apply",
|
|
735
|
+
...reEnrichFlag,
|
|
693
736
|
});
|
|
694
737
|
continue;
|
|
695
738
|
}
|
|
@@ -700,8 +743,12 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
700
743
|
sleepMs(delayMs);
|
|
701
744
|
}
|
|
702
745
|
applyWritesSinceSleep += 1;
|
|
746
|
+
// Additive-only: remove list is always empty (never strip labels; #3197).
|
|
703
747
|
client.apply(repo, issueNumber, add, []);
|
|
704
748
|
applied += 1;
|
|
749
|
+
if (isReEnrichRow) {
|
|
750
|
+
reEnrichApplied += 1;
|
|
751
|
+
}
|
|
705
752
|
items.push({
|
|
706
753
|
repo,
|
|
707
754
|
issue_number: issueNumber,
|
|
@@ -713,6 +760,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
713
760
|
desired,
|
|
714
761
|
add,
|
|
715
762
|
status: "applied",
|
|
763
|
+
...reEnrichFlag,
|
|
716
764
|
});
|
|
717
765
|
}
|
|
718
766
|
catch (exc) {
|
|
@@ -730,6 +778,7 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
730
778
|
add,
|
|
731
779
|
status: "error",
|
|
732
780
|
message: formatMissingLabelHint(rawMsg, add),
|
|
781
|
+
...reEnrichFlag,
|
|
733
782
|
});
|
|
734
783
|
// Partial failure: continue remaining issues (idempotent re-run skips applied).
|
|
735
784
|
}
|
|
@@ -746,6 +795,8 @@ export function mirrorLabels(projectRoot, options) {
|
|
|
746
795
|
skipped_unreadable: skippedUnreadable,
|
|
747
796
|
skipped_closed: skippedClosed,
|
|
748
797
|
skipped_author: skippedAuthor,
|
|
798
|
+
re_enrich_planned: reEnrichPlanned,
|
|
799
|
+
re_enrich_applied: reEnrichApplied,
|
|
749
800
|
errors,
|
|
750
801
|
digest,
|
|
751
802
|
items,
|
|
@@ -763,16 +814,27 @@ function formatCountMap(map) {
|
|
|
763
814
|
export function renderLabelMirrorReport(outcome) {
|
|
764
815
|
const lines = [];
|
|
765
816
|
const mode = outcome.dry_run ? "dry-run" : "apply";
|
|
766
|
-
|
|
817
|
+
const reEnrichOn = outcome.filters.re_enrich === true;
|
|
818
|
+
lines.push(reEnrichOn
|
|
819
|
+
? `triage:classify --mirror --re-enrich (${mode}) — additive re-enrich (#3197 / #1423)`
|
|
820
|
+
: `triage:classify --mirror (${mode}) — bootstrap mass-triage (#1423 Wave 2)`);
|
|
767
821
|
const stateFilter = outcome.filters.include_closed ? "all (include-closed)" : "open-only";
|
|
768
822
|
const repoPart = outcome.filters.repo ?? "*";
|
|
769
823
|
const authorPart = outcome.filters.author ?? "*";
|
|
770
|
-
|
|
824
|
+
const reEnrichPart = reEnrichOn ? "on (additive-only)" : "off";
|
|
825
|
+
lines.push(`filters: state=${stateFilter} repo=${repoPart} author=${authorPart} re_enrich=${reEnrichPart}`);
|
|
826
|
+
const firstTimePlanned = Math.max(0, outcome.planned - outcome.re_enrich_planned);
|
|
827
|
+
const firstTimeApplied = Math.max(0, outcome.applied - outcome.re_enrich_applied);
|
|
771
828
|
lines.push(`scanned=${outcome.scanned} planned=${outcome.planned} applied=${outcome.applied} ` +
|
|
772
829
|
`unchanged=${outcome.unchanged} already_triaged=${outcome.skipped_already_triaged} ` +
|
|
773
830
|
`no_match=${outcome.skipped_no_match} closed_skipped=${outcome.skipped_closed} ` +
|
|
774
831
|
`author_skipped=${outcome.skipped_author} ` +
|
|
775
832
|
`unreadable=${outcome.skipped_unreadable} errors=${outcome.errors}`);
|
|
833
|
+
// Distinguish first-time stamp rows vs re-enrich additive backfill (#3197 / #3124 re-run vs re-enrich).
|
|
834
|
+
lines.push(`planned_kind: first_time=${firstTimePlanned} re_enrich=${outcome.re_enrich_planned}` +
|
|
835
|
+
(outcome.dry_run
|
|
836
|
+
? ""
|
|
837
|
+
: ` | applied_kind: first_time=${firstTimeApplied} re_enrich=${outcome.re_enrich_applied}`));
|
|
776
838
|
lines.push(`idempotencyLabel=${outcome.policy.idempotencyLabel} alwaysLabels=${JSON.stringify(outcome.policy.alwaysLabels)}`);
|
|
777
839
|
if (!outcome.dry_run) {
|
|
778
840
|
lines.push(`apply: batch_size=${outcome.batch_size ?? DEFAULT_APPLY_BATCH_SIZE} delay_ms=${outcome.delay_ms ?? DEFAULT_APPLY_DELAY_MS}`);
|
|
@@ -797,8 +859,9 @@ export function renderLabelMirrorReport(outcome) {
|
|
|
797
859
|
const actionPart = item.action !== null ? ` action=${item.action}` : "";
|
|
798
860
|
const rulePart = item.ruleKind !== null ? ` rule=${item.ruleKind}` : "";
|
|
799
861
|
const statePart = item.state !== null ? ` state=${item.state}` : "";
|
|
862
|
+
const kindPart = item.re_enrich === true ? " kind=re-enrich" : " kind=first-time";
|
|
800
863
|
const addPart = sanitizeReportFragment(item.add.join(", +"));
|
|
801
|
-
lines.push(`- ${item.repo}#${item.issue_number}:${statePart}${actionPart}${rulePart} +${addPart}`);
|
|
864
|
+
lines.push(`- ${item.repo}#${item.issue_number}:${statePart}${actionPart}${rulePart}${kindPart} +${addPart}`);
|
|
802
865
|
}
|
|
803
866
|
if (outcome.digest.sample_truncated) {
|
|
804
867
|
const remaining = writeTotal - outcome.digest.samples.length;
|
|
@@ -817,6 +880,9 @@ export function renderLabelMirrorReport(outcome) {
|
|
|
817
880
|
if (already.length > 0) {
|
|
818
881
|
lines.push("");
|
|
819
882
|
lines.push(`Skipped (already ${outcome.policy.idempotencyLabel}): ${already.length}`);
|
|
883
|
+
if (!reEnrichOn) {
|
|
884
|
+
lines.push(" Tip: after actionLabels / rule changes, use --mirror --re-enrich (dry-run) to plan additive chips on already-stamped issues (#3197; re-run vs re-enrich #3124).");
|
|
885
|
+
}
|
|
820
886
|
}
|
|
821
887
|
const noMatch = outcome.items.filter((i) => i.status === "skipped_no_match");
|
|
822
888
|
if (noMatch.length > 0) {
|
|
@@ -833,7 +899,9 @@ export function renderLabelMirrorReport(outcome) {
|
|
|
833
899
|
}
|
|
834
900
|
if (outcome.dry_run && writeTotal > 0) {
|
|
835
901
|
lines.push("");
|
|
836
|
-
lines.push(
|
|
902
|
+
lines.push(reEnrichOn
|
|
903
|
+
? "Dry-run — re-run with --mirror --re-enrich --apply to write additive labels via SCM (batched; never triage:accept; never removals)."
|
|
904
|
+
: "Dry-run — re-run with --mirror --apply to write these labels via SCM (batched; never triage:accept).");
|
|
837
905
|
}
|
|
838
906
|
return `${lines.join("\n")}\n`;
|
|
839
907
|
}
|
|
@@ -851,12 +919,15 @@ export function labelMirrorOutcomeToJson(outcome) {
|
|
|
851
919
|
skipped_unreadable: outcome.skipped_unreadable,
|
|
852
920
|
skipped_closed: outcome.skipped_closed,
|
|
853
921
|
skipped_author: outcome.skipped_author,
|
|
922
|
+
re_enrich_planned: outcome.re_enrich_planned,
|
|
923
|
+
re_enrich_applied: outcome.re_enrich_applied,
|
|
854
924
|
errors: outcome.errors,
|
|
855
925
|
filters: {
|
|
856
926
|
include_closed: outcome.filters.include_closed,
|
|
857
927
|
repo: outcome.filters.repo,
|
|
858
928
|
author: outcome.filters.author,
|
|
859
929
|
author_logins: outcome.filters.author_logins,
|
|
930
|
+
re_enrich: outcome.filters.re_enrich,
|
|
860
931
|
},
|
|
861
932
|
digest: {
|
|
862
933
|
by_state: { ...outcome.digest.by_state },
|
|
@@ -890,6 +961,7 @@ function itemToJson(i) {
|
|
|
890
961
|
add: [...i.add],
|
|
891
962
|
status: i.status,
|
|
892
963
|
...(i.message !== undefined ? { message: i.message } : {}),
|
|
964
|
+
...(i.re_enrich === true ? { re_enrich: true } : {}),
|
|
893
965
|
};
|
|
894
966
|
}
|
|
895
967
|
/** Validate triageLabelMirror on a plan object (vbrief_validate hook). */
|
|
@@ -169,12 +169,12 @@ export declare const registryData: {
|
|
|
169
169
|
readonly "task triage:classify": {
|
|
170
170
|
readonly name: "task triage:classify";
|
|
171
171
|
readonly summary: "Inspect / validate auto-classification; bootstrap mass-triage label mirror";
|
|
172
|
-
readonly refs: "(D10 / #1129, #1423 Wave 1–2 / #3125, #3129)";
|
|
173
|
-
readonly description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror is the bootstrap mass-triage entrypoint (#3125): classifies the github-issue cache and mirrors outcomes as SCM labels. Open-only by default (opt-in --include-closed); optional --author LOGIN scopes plan/apply to matching author.login (AND with open-only; #3129);
|
|
174
|
-
readonly usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--include-closed] [--author LOGIN|@me] [--repo owner/name] [--batch-size N] [--delay-ms N] [--sample-limit N] [--json] [--allow-cross-repo]]";
|
|
175
|
-
readonly flags: readonly [readonly ["--list", "(default)", "Print effective rules + hold markers."], readonly ["--validate", "(off)", "Validate plan.policy.triageAutoClassify + triageLabelMirror."], readonly ["--mirror", "(off)", "Bootstrap mass-triage label mirror: classify cache → planned/applied labels (dry-run default, open-only)."], readonly ["--apply", "(off)", "With --mirror: write labels via SCM (batched; requires github SCM boundary)."], readonly ["--include-closed", "(off)", "With --mirror: include closed issues (default open-only avoids archive mass-stamp)."], readonly ["--author LOGIN|@me", "(none)", "With --mirror: only plan/apply issues whose cache author.login matches (exact; @me; comma allow-list) (#3129)."], readonly ["--author-mine", "(off)", "With --mirror: alias for --author @me (#3129)."], readonly ["--repo owner/name", "(all cached)", "Limit mirror to one repo."], readonly ["--batch-size N", "10", "With --mirror --apply: writes per batch before delay."], readonly ["--delay-ms N", "1000", "With --mirror --apply: ms sleep between batches (rate-limit)."], readonly ["--sample-limit N", "15", "With --mirror: max samples in human digest."], readonly ["--json", "(off)", "With --mirror: structured JSON outcome including digest aggregates."], readonly ["--allow-cross-repo", "(off)", "With --mirror --apply: allow non-project repos (#2601)."]];
|
|
176
|
-
readonly examples: readonly ["task triage:classify -- --list", "task triage:classify -- --validate", "task triage:classify -- --mirror", "task triage:classify -- --mirror --author @me --repo owner/name", "task triage:classify -- --mirror --repo owner/name --json", "task triage:classify -- --mirror --apply --repo owner/name --batch-size 10 --delay-ms 1000", "task triage:classify -- --mirror --include-closed --repo owner/name"];
|
|
177
|
-
readonly see_also: readonly ["task triage:bootstrap", "task triage:queue", "task vbrief:reconcile:labels", "#1119 / D10", "#1423", "#3125", "#3129"];
|
|
172
|
+
readonly refs: "(D10 / #1129, #1423 Wave 1–2 / #3125, #3129, #3197)";
|
|
173
|
+
readonly description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror is the bootstrap mass-triage entrypoint (#3125): classifies the github-issue cache and mirrors outcomes as SCM labels. Open-only by default (opt-in --include-closed); optional --author LOGIN scopes plan/apply to matching author.login (AND with open-only; #3129). Default re-run skips already-stamped (idempotency) issues; after policy/rule changes use --re-enrich to re-classify already-triaged issues and plan additive label deltas only (#3197; re-run vs re-enrich discovery #3124). Dry-run digest shows totals + by state/rule/action + samples, first-time vs re-enrich planned rows, and surfaces the author filter; --apply batches writes with rate-limit delay. Never calls triage:accept / never writes proposed/ xBRIEFs.";
|
|
174
|
+
readonly usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--re-enrich] [--include-closed] [--author LOGIN|@me] [--repo owner/name] [--batch-size N] [--delay-ms N] [--sample-limit N] [--json] [--allow-cross-repo]]";
|
|
175
|
+
readonly flags: readonly [readonly ["--list", "(default)", "Print effective rules + hold markers."], readonly ["--validate", "(off)", "Validate plan.policy.triageAutoClassify + triageLabelMirror."], readonly ["--mirror", "(off)", "Bootstrap mass-triage label mirror: classify cache → planned/applied labels (dry-run default, open-only)."], readonly ["--apply", "(off)", "With --mirror: write labels via SCM (batched; requires github SCM boundary)."], readonly ["--re-enrich", "(off)", "With --mirror: re-classify already-stamped issues; plan additive label deltas only (dry-run default; #3197 / #3124 re-run vs re-enrich)."], readonly ["--include-closed", "(off)", "With --mirror: include closed issues (default open-only avoids archive mass-stamp)."], readonly ["--author LOGIN|@me", "(none)", "With --mirror: only plan/apply issues whose cache author.login matches (exact; @me; comma allow-list) (#3129)."], readonly ["--author-mine", "(off)", "With --mirror: alias for --author @me (#3129)."], readonly ["--repo owner/name", "(all cached)", "Limit mirror to one repo."], readonly ["--batch-size N", "10", "With --mirror --apply: writes per batch before delay."], readonly ["--delay-ms N", "1000", "With --mirror --apply: ms sleep between batches (rate-limit)."], readonly ["--sample-limit N", "15", "With --mirror: max samples in human digest."], readonly ["--json", "(off)", "With --mirror: structured JSON outcome including digest aggregates."], readonly ["--allow-cross-repo", "(off)", "With --mirror --apply: allow non-project repos (#2601)."]];
|
|
176
|
+
readonly examples: readonly ["task triage:classify -- --list", "task triage:classify -- --validate", "task triage:classify -- --mirror", "task triage:classify -- --mirror --author @me --repo owner/name", "task triage:classify -- --mirror --repo owner/name --json", "task triage:classify -- --mirror --apply --repo owner/name --batch-size 10 --delay-ms 1000", "task triage:classify -- --mirror --re-enrich --repo owner/name", "task triage:classify -- --mirror --re-enrich --apply --repo owner/name", "task triage:classify -- --mirror --include-closed --repo owner/name"];
|
|
177
|
+
readonly see_also: readonly ["task triage:bootstrap", "task triage:queue", "task vbrief:reconcile:labels", "#1119 / D10", "#1423", "#3124", "#3125", "#3129", "#3197"];
|
|
178
178
|
readonly placeholder: false;
|
|
179
179
|
};
|
|
180
180
|
readonly "task triage:bootstrap": {
|
|
@@ -278,9 +278,9 @@ export const registryData = {
|
|
|
278
278
|
"task triage:classify": {
|
|
279
279
|
name: "task triage:classify",
|
|
280
280
|
summary: "Inspect / validate auto-classification; bootstrap mass-triage label mirror",
|
|
281
|
-
refs: "(D10 / #1129, #1423 Wave 1–2 / #3125, #3129)",
|
|
282
|
-
description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror is the bootstrap mass-triage entrypoint (#3125): classifies the github-issue cache and mirrors outcomes as SCM labels. Open-only by default (opt-in --include-closed); optional --author LOGIN scopes plan/apply to matching author.login (AND with open-only; #3129);
|
|
283
|
-
usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--include-closed] [--author LOGIN|@me] [--repo owner/name] [--batch-size N] [--delay-ms N] [--sample-limit N] [--json] [--allow-cross-repo]]",
|
|
281
|
+
refs: "(D10 / #1129, #1423 Wave 1–2 / #3125, #3129, #3197)",
|
|
282
|
+
description: "Inspect or validate the auto-classification rule set. --list renders effective rules (framework universal first, consumer overrides next). --validate exits non-zero on a malformed plan.policy.triageAutoClassify or triageLabelMirror. --mirror is the bootstrap mass-triage entrypoint (#3125): classifies the github-issue cache and mirrors outcomes as SCM labels. Open-only by default (opt-in --include-closed); optional --author LOGIN scopes plan/apply to matching author.login (AND with open-only; #3129). Default re-run skips already-stamped (idempotency) issues; after policy/rule changes use --re-enrich to re-classify already-triaged issues and plan additive label deltas only (#3197; re-run vs re-enrich discovery #3124). Dry-run digest shows totals + by state/rule/action + samples, first-time vs re-enrich planned rows, and surfaces the author filter; --apply batches writes with rate-limit delay. Never calls triage:accept / never writes proposed/ xBRIEFs.",
|
|
283
|
+
usage: "task triage:classify -- [--list | --validate | --mirror [--apply] [--re-enrich] [--include-closed] [--author LOGIN|@me] [--repo owner/name] [--batch-size N] [--delay-ms N] [--sample-limit N] [--json] [--allow-cross-repo]]",
|
|
284
284
|
flags: [
|
|
285
285
|
["--list", "(default)", "Print effective rules + hold markers."],
|
|
286
286
|
["--validate", "(off)", "Validate plan.policy.triageAutoClassify + triageLabelMirror."],
|
|
@@ -294,6 +294,11 @@ export const registryData = {
|
|
|
294
294
|
"(off)",
|
|
295
295
|
"With --mirror: write labels via SCM (batched; requires github SCM boundary).",
|
|
296
296
|
],
|
|
297
|
+
[
|
|
298
|
+
"--re-enrich",
|
|
299
|
+
"(off)",
|
|
300
|
+
"With --mirror: re-classify already-stamped issues; plan additive label deltas only (dry-run default; #3197 / #3124 re-run vs re-enrich).",
|
|
301
|
+
],
|
|
297
302
|
[
|
|
298
303
|
"--include-closed",
|
|
299
304
|
"(off)",
|
|
@@ -319,6 +324,8 @@ export const registryData = {
|
|
|
319
324
|
"task triage:classify -- --mirror --author @me --repo owner/name",
|
|
320
325
|
"task triage:classify -- --mirror --repo owner/name --json",
|
|
321
326
|
"task triage:classify -- --mirror --apply --repo owner/name --batch-size 10 --delay-ms 1000",
|
|
327
|
+
"task triage:classify -- --mirror --re-enrich --repo owner/name",
|
|
328
|
+
"task triage:classify -- --mirror --re-enrich --apply --repo owner/name",
|
|
322
329
|
"task triage:classify -- --mirror --include-closed --repo owner/name",
|
|
323
330
|
],
|
|
324
331
|
see_also: [
|
|
@@ -327,8 +334,10 @@ export const registryData = {
|
|
|
327
334
|
"task vbrief:reconcile:labels",
|
|
328
335
|
"#1119 / D10",
|
|
329
336
|
"#1423",
|
|
337
|
+
"#3124",
|
|
330
338
|
"#3125",
|
|
331
339
|
"#3129",
|
|
340
|
+
"#3197",
|
|
332
341
|
],
|
|
333
342
|
placeholder: false,
|
|
334
343
|
},
|
|
@@ -14,6 +14,10 @@ export declare function validateHostSlashCommandsOnPlan(plan: unknown, filepath:
|
|
|
14
14
|
export declare function validateOpenClawProductCommandsOnPlan(plan: unknown, filepath: string): string[];
|
|
15
15
|
/** vbrief_validate hook: validate ``plan.policy.stalenessTickler`` (#2489). */
|
|
16
16
|
export declare function validateStalenessTicklerOnPlan(plan: unknown, filepath: string): string[];
|
|
17
|
+
/** vbrief_validate hook: validate ``plan.policy.coverageDebt`` (#3189). */
|
|
18
|
+
export declare function validateCoverageDebtOnPlan(plan: unknown, filepath: string): string[];
|
|
19
|
+
/** vbrief_validate hook: validate ``plan.policy.checkResume`` (#3189). */
|
|
20
|
+
export declare function validateCheckResumeOnPlan(plan: unknown, filepath: string): string[];
|
|
17
21
|
/** Run all PROJECT-DEFINITION policy hooks (mirrors lazy-import block in Python). */
|
|
18
22
|
export declare function runProjectDefinitionHooks(plan: unknown, filepath: string): string[];
|
|
19
23
|
//# sourceMappingURL=plan-hooks.d.ts.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { validateCheckResume } from "../policy/check-resume.js";
|
|
2
|
+
import { validateCoverageDebt } from "../policy/coverage-debt.js";
|
|
1
3
|
import { validateHostHooks } from "../policy/host-hooks.js";
|
|
2
4
|
import { validateHostSlashCommands } from "../policy/host-slash-commands.js";
|
|
3
5
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
@@ -196,6 +198,51 @@ export function validateStalenessTicklerOnPlan(plan, filepath) {
|
|
|
196
198
|
}
|
|
197
199
|
return out;
|
|
198
200
|
}
|
|
201
|
+
/** vbrief_validate hook: validate ``plan.policy.coverageDebt`` (#3189). */
|
|
202
|
+
export function validateCoverageDebtOnPlan(plan, filepath) {
|
|
203
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const policy = readPlanPolicy(plan);
|
|
207
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
if (!("coverageDebt" in policy)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
const raw = policy.coverageDebt;
|
|
214
|
+
// Explicit null is a typed key with invalid value — fail closed at validation time.
|
|
215
|
+
if (raw === null) {
|
|
216
|
+
return [`${filepath}: plan.policy.coverageDebt must be an object; got null (#3189)`];
|
|
217
|
+
}
|
|
218
|
+
const out = [];
|
|
219
|
+
for (const err of validateCoverageDebt(raw)) {
|
|
220
|
+
out.push(`${filepath}: ${err} (#3189)`);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
/** vbrief_validate hook: validate ``plan.policy.checkResume`` (#3189). */
|
|
225
|
+
export function validateCheckResumeOnPlan(plan, filepath) {
|
|
226
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
const policy = readPlanPolicy(plan);
|
|
230
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (!("checkResume" in policy)) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
const raw = policy.checkResume;
|
|
237
|
+
if (raw === null) {
|
|
238
|
+
return [`${filepath}: plan.policy.checkResume must be an object; got null (#3189)`];
|
|
239
|
+
}
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const err of validateCheckResume(raw)) {
|
|
242
|
+
out.push(`${filepath}: ${err} (#3189)`);
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
199
246
|
/** Run all PROJECT-DEFINITION policy hooks (mirrors lazy-import block in Python). */
|
|
200
247
|
export function runProjectDefinitionHooks(plan, filepath) {
|
|
201
248
|
const errors = [];
|
|
@@ -266,6 +313,13 @@ export function runProjectDefinitionHooks(plan, filepath) {
|
|
|
266
313
|
catch {
|
|
267
314
|
/* hook must not break validation */
|
|
268
315
|
}
|
|
316
|
+
try {
|
|
317
|
+
errors.push(...validateCoverageDebtOnPlan(plan, filepath));
|
|
318
|
+
errors.push(...validateCheckResumeOnPlan(plan, filepath));
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
/* hook must not break validation */
|
|
322
|
+
}
|
|
269
323
|
return errors;
|
|
270
324
|
}
|
|
271
325
|
//# sourceMappingURL=plan-hooks.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.98.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -354,8 +354,8 @@
|
|
|
354
354
|
"provenance": true
|
|
355
355
|
},
|
|
356
356
|
"dependencies": {
|
|
357
|
-
"@deftai/directive-content": "^0.
|
|
358
|
-
"@deftai/directive-types": "^0.
|
|
357
|
+
"@deftai/directive-content": "^0.98.0",
|
|
358
|
+
"@deftai/directive-types": "^0.98.0",
|
|
359
359
|
"archiver": "^8.0.0"
|
|
360
360
|
},
|
|
361
361
|
"scripts": {
|