@deftai/directive-core 0.97.0 → 0.98.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authz/classify.js +443 -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/scope-provenance/evaluate.d.ts +21 -0
- package/dist/scope-provenance/evaluate.js +143 -33
- package/dist/scope-provenance/index.d.ts +1 -1
- package/dist/scope-provenance/index.js +1 -1
- 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
|
@@ -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.1",
|
|
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.1",
|
|
358
|
+
"@deftai/directive-types": "^0.98.1",
|
|
359
359
|
"archiver": "^8.0.0"
|
|
360
360
|
},
|
|
361
361
|
"scripts": {
|