@mstar-harness/engine 2.2.0 → 2.4.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/engine.js +72 -5
- package/dist/index.d.ts +4 -4
- package/dist/lint.d.ts +43 -0
- package/dist/skill-authoring.d.ts +27 -3
- package/package.json +1 -1
package/dist/engine.js
CHANGED
|
@@ -674,7 +674,7 @@ function parseAssignmentBranchForms(assignmentText) {
|
|
|
674
674
|
if (fields.branchPolicy !== undefined && fields.branchPolicy !== "") {
|
|
675
675
|
const direct = fields.branchPolicy.match(/^direct\s+on\s+(\S+)/i);
|
|
676
676
|
if (direct) {
|
|
677
|
-
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[
|
|
677
|
+
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[\u2014\u2013]|--|-)\s*(.+))?$/);
|
|
678
678
|
forms.directOn = { branch: direct[1].trim(), reason: strict ? (strict[2] ?? "").trim() : "" };
|
|
679
679
|
}
|
|
680
680
|
}
|
|
@@ -2552,6 +2552,7 @@ function renderIndex(params) {
|
|
|
2552
2552
|
if (rejectedRows !== "") {
|
|
2553
2553
|
sections.push("", "## Findings considered and rejected", "", rejectedRows);
|
|
2554
2554
|
}
|
|
2555
|
+
sections.push("", "## Red-team dispositions", "", "- <finding>: <survived / refuted / hallucination-dropped / uncovered-kept>, <one-line reason>");
|
|
2555
2556
|
return `${sections.join(`
|
|
2556
2557
|
`)}
|
|
2557
2558
|
`;
|
|
@@ -3080,11 +3081,31 @@ function findTemporaryMarkers(fileText) {
|
|
|
3080
3081
|
}
|
|
3081
3082
|
return { ok: violations.length === 0, violations, markers };
|
|
3082
3083
|
}
|
|
3084
|
+
var TASK_ARTIFACT_RE = /\btask-\d+(?:-(?:brief|report|fix-report|diff)|\.diff)\b/g;
|
|
3085
|
+
var SDD_DEEPLINK_RE = /\.(?:mstar|agents)\/sdd\/([^\s/<>{}\[\]"'\*\?]+)/g;
|
|
3086
|
+
function findEphemeralCitations(skillText) {
|
|
3087
|
+
const citations = [];
|
|
3088
|
+
const lines = skillText.split(/\r?\n/);
|
|
3089
|
+
for (let i = 0;i < lines.length; i++) {
|
|
3090
|
+
const found = [];
|
|
3091
|
+
for (const m of lines[i].matchAll(TASK_ARTIFACT_RE)) {
|
|
3092
|
+
found.push({ index: m.index, match: m[0], kind: "task-artifact" });
|
|
3093
|
+
}
|
|
3094
|
+
for (const m of lines[i].matchAll(SDD_DEEPLINK_RE)) {
|
|
3095
|
+
found.push({ index: m.index, match: m[0], kind: "sdd-deeplink" });
|
|
3096
|
+
}
|
|
3097
|
+
found.sort((a, b) => a.index - b.index);
|
|
3098
|
+
for (const f of found) {
|
|
3099
|
+
citations.push({ line: i + 1, match: f.match, kind: f.kind });
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
return citations;
|
|
3103
|
+
}
|
|
3083
3104
|
var TEST_FILE_PATH_RE = /[\w./-]+\.(?:test|spec)\.[a-z0-9]+/i;
|
|
3084
3105
|
var TEST_FILE_PHRASE_RE = /\btest files?\b/i;
|
|
3085
3106
|
var COMMAND_PROMPT_RE = /^\s*[$>]\s*\S/;
|
|
3086
3107
|
var RUNNER_RE = /\b(?:bun|pnpm|npm|yarn|npx|bunx)\s+(?:test|run|exec)\b|\b(?:npx|bunx)\s+[\w./-]+\b|\b(?:tsc|vitest|jest|mocha|pytest)\b|\bgo\s+test\b|\bcargo\s+test\b/i;
|
|
3087
|
-
var OUTPUT_TOKEN_RE = /[
|
|
3108
|
+
var OUTPUT_TOKEN_RE = /[\u2713\u2714\u2717\u2718]|\b(?:PASS|FAIL)\b|\b\d+\s+(?:pass(?:es|ed)?|fail(?:s|ed|ing)?|skipped|tests?|ok)\b|\bok\s+\d+\b|\ball\s+ok\b|exit(?:ed)?\s+(?:with\s+)?(?:code\s+)?\d+/i;
|
|
3088
3109
|
function assertSddTddTriple(reportText) {
|
|
3089
3110
|
const violations = [];
|
|
3090
3111
|
const lines = reportText.split(/\r?\n/);
|
|
@@ -3447,13 +3468,56 @@ var FIVE_QUESTION_SECTIONS = [
|
|
|
3447
3468
|
{ key: "evidence", label: "Evidence", question: "what a correct result looks like (success criteria / evidence)" },
|
|
3448
3469
|
{ key: "references", label: "References", question: "additional resources to open when the main path is not enough" }
|
|
3449
3470
|
];
|
|
3471
|
+
function stripFrontmatter(text) {
|
|
3472
|
+
const lines = text.replace(/^\uFEFF/, "").split(/\r?\n/);
|
|
3473
|
+
if (lines.length === 0 || lines[0].trim() !== "---")
|
|
3474
|
+
return text;
|
|
3475
|
+
for (let i = 1;i < lines.length; i++) {
|
|
3476
|
+
if (lines[i].trim() === "---")
|
|
3477
|
+
return lines.slice(i + 1).join(`
|
|
3478
|
+
`);
|
|
3479
|
+
}
|
|
3480
|
+
return text;
|
|
3481
|
+
}
|
|
3450
3482
|
var HEADING_RE = /^#{1,6}\s+[^\r\n]+$/;
|
|
3451
|
-
function
|
|
3452
|
-
const headings =
|
|
3483
|
+
function collectHeadings(bodyText) {
|
|
3484
|
+
const headings = [];
|
|
3485
|
+
let inFence = false;
|
|
3486
|
+
for (const line of bodyText.split(/\r?\n/)) {
|
|
3487
|
+
if (line.startsWith("```") || line.startsWith("~~~")) {
|
|
3488
|
+
inFence = !inFence;
|
|
3489
|
+
continue;
|
|
3490
|
+
}
|
|
3491
|
+
if (!inFence && HEADING_RE.test(line)) {
|
|
3492
|
+
headings.push(line.replace(/^#{1,6}\s+/, "").trim().toLowerCase());
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return headings;
|
|
3496
|
+
}
|
|
3497
|
+
var RUNTIME_HEADING_ALIASES = {
|
|
3498
|
+
workflow: ["process", "playbook"],
|
|
3499
|
+
"decision-rules": [
|
|
3500
|
+
"hard rules",
|
|
3501
|
+
"core rules",
|
|
3502
|
+
"rule",
|
|
3503
|
+
"gate",
|
|
3504
|
+
"not to do",
|
|
3505
|
+
"red flags",
|
|
3506
|
+
"反模式",
|
|
3507
|
+
"红线",
|
|
3508
|
+
"规则",
|
|
3509
|
+
"门禁"
|
|
3510
|
+
],
|
|
3511
|
+
evidence: ["output format", "证据"],
|
|
3512
|
+
references: ["dependencies", "关系"]
|
|
3513
|
+
};
|
|
3514
|
+
function lintFiveQuestion(bodyText, mode = "authoring") {
|
|
3515
|
+
const headings = collectHeadings(bodyText);
|
|
3453
3516
|
const violations = [];
|
|
3454
3517
|
for (const section of FIVE_QUESTION_SECTIONS) {
|
|
3455
3518
|
const label = section.label.toLowerCase();
|
|
3456
|
-
const
|
|
3519
|
+
const aliases = mode === "runtime" ? RUNTIME_HEADING_ALIASES[section.key] ?? [] : [];
|
|
3520
|
+
const covered = headings.some((heading) => heading.includes(label) || aliases.some((alias) => heading.includes(alias)));
|
|
3457
3521
|
if (!covered) {
|
|
3458
3522
|
violations.push(violation11("low", `skill-authoring.five-question.${section.key}`, `body does not answer "${section.question}" — no "${section.label}" section (mstar-skill-authoring § Body 必须回答的 5 问 / § 默认 Body 结构)`, `add a "## ${section.label}" section covering ${section.question}`));
|
|
3459
3523
|
}
|
|
@@ -3482,6 +3546,7 @@ export {
|
|
|
3482
3546
|
techDebtRollup,
|
|
3483
3547
|
taskReportExists,
|
|
3484
3548
|
taskBrief,
|
|
3549
|
+
stripFrontmatter,
|
|
3485
3550
|
singleReviewSnapshot,
|
|
3486
3551
|
sddWorkspace,
|
|
3487
3552
|
scopeGuard,
|
|
@@ -3526,6 +3591,7 @@ export {
|
|
|
3526
3591
|
findingsCleanupGate,
|
|
3527
3592
|
findTemporaryMarkers,
|
|
3528
3593
|
findSimplifyMarkers,
|
|
3594
|
+
findEphemeralCitations,
|
|
3529
3595
|
executionModeToN,
|
|
3530
3596
|
evaluatePhaseGate,
|
|
3531
3597
|
emitGitignoreSnippet,
|
|
@@ -3553,6 +3619,7 @@ export {
|
|
|
3553
3619
|
SddScriptError,
|
|
3554
3620
|
SHARED_FAMILIES,
|
|
3555
3621
|
SEVERITY_ORDER,
|
|
3622
|
+
RUNTIME_HEADING_ALIASES,
|
|
3556
3623
|
ROLE_MAPPING,
|
|
3557
3624
|
QC_REVIEWER_PARAMS,
|
|
3558
3625
|
KNOWLEDGE_SEVERITIES,
|
package/dist/index.d.ts
CHANGED
|
@@ -42,11 +42,11 @@ export type { AuditCategory, AuditEffort, AuditFinding, AuditPriority, AuditRisk
|
|
|
42
42
|
export { AUDIT_CATEGORIES, AUDIT_EFFORTS, AUDIT_PRIORITIES, AUDIT_RISKS, redactSecrets, scaffoldAuditPlan, validateAuditStatusBlocks, } from "./audit.js";
|
|
43
43
|
export type { ReferenceCheckResult } from "./compound.js";
|
|
44
44
|
export { KNOWLEDGE_BUG_PROBLEM_TYPES, KNOWLEDGE_CATEGORY_MAP, KNOWLEDGE_KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_REQUIRED_FIELDS, KNOWLEDGE_RESOLUTION_TYPES, KNOWLEDGE_SEVERITIES, assertIndexRows, compoundRefreshScope, referenceExists, scopeGuard, validateSchemaYaml, } from "./compound.js";
|
|
45
|
-
export type { PlanQualityFinding, PlanQualityResult, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
|
|
46
|
-
export { assertSddTddTriple, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
|
|
45
|
+
export type { EphemeralCitation, PlanQualityFinding, PlanQualityResult, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
|
|
46
|
+
export { assertSddTddTriple, findEphemeralCitations, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
|
|
47
47
|
export type { DevTrackParam, QcReviewerParam, RoleFamily, RoleMappingEntry, RoleMappingOptions, } from "./roles.js";
|
|
48
48
|
export { DEV_TRACK_PARAMS, QC_REVIEWER_PARAMS, ROLE_MAPPING, SHARED_FAMILIES, lintLoadOrder, validateRoleMapping, } from "./roles.js";
|
|
49
49
|
export type { DetectResult, HostAdapter, HostId, SkillRootPaths, ToolSignal } from "./host.js";
|
|
50
50
|
export { detectHost, resolveSkillRoot } from "./host.js";
|
|
51
|
-
export type { FiveQuestionSection } from "./skill-authoring.js";
|
|
52
|
-
export { FIVE_QUESTION_SECTIONS, lintFiveQuestion, lintFrontmatter, resolveAssetPath, } from "./skill-authoring.js";
|
|
51
|
+
export type { FiveQuestionMode, FiveQuestionSection } from "./skill-authoring.js";
|
|
52
|
+
export { FIVE_QUESTION_SECTIONS, RUNTIME_HEADING_ALIASES, lintFiveQuestion, lintFrontmatter, resolveAssetPath, stripFrontmatter, } from "./skill-authoring.js";
|
package/dist/lint.d.ts
CHANGED
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
* contract (not a workflow summary), third person.
|
|
28
28
|
* - STRATEGY.md structure: `mstar-strategy` SKILL.md § STRATEGY.md structure —
|
|
29
29
|
* six required sections.
|
|
30
|
+
* - Ephemeral citations: knowledge `conventions/skill-content-porting-discipline.md`
|
|
31
|
+
* §3 ("No ephemeral citations in durable skill text") + session evaluation
|
|
32
|
+
* 2026-08-16 discrimination contract — concrete task-artifact references
|
|
33
|
+
* and SDD deeplinks are ephemeral; placeholder forms are not.
|
|
30
34
|
*
|
|
31
35
|
* Enforcement depth: roadmap §8.5 C4 — v1 lints are non-blocking
|
|
32
36
|
* `ValidationResult`s; callers surface them as warnings.
|
|
@@ -95,6 +99,45 @@ export type TemporaryMarkerResult = GateResult & {
|
|
|
95
99
|
* carry); do not special-case it without a real FP report.
|
|
96
100
|
*/
|
|
97
101
|
export declare function findTemporaryMarkers(fileText: string): TemporaryMarkerResult;
|
|
102
|
+
/**
|
|
103
|
+
* An ephemeral citation found in durable skill text: a concrete reference to
|
|
104
|
+
* a per-task artifact or an SDD deeplink that survives nothing (knowledge
|
|
105
|
+
* conventions/skill-content-porting-discipline.md §3 — "No ephemeral
|
|
106
|
+
* citations in durable skill text": a calibration line citing an SDD task
|
|
107
|
+
* report violates standalone + survives nothing; instances/examples cite
|
|
108
|
+
* in-repo artifacts only).
|
|
109
|
+
*/
|
|
110
|
+
export type EphemeralCitation = {
|
|
111
|
+
/** 1-based line number of the citation. */
|
|
112
|
+
line: number;
|
|
113
|
+
/** The matched citation token (artifact name or deeplink prefix). */
|
|
114
|
+
match: string;
|
|
115
|
+
/** `task-artifact`: `task-<digits>-(brief|report|fix-report|diff)`;
|
|
116
|
+
* `sdd-deeplink`: `.mstar/sdd/` / `.agents/sdd/` + a concrete first
|
|
117
|
+
* segment. */
|
|
118
|
+
kind: "task-artifact" | "sdd-deeplink";
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Find ephemeral citations in skill text (knowledge
|
|
122
|
+
* conventions/skill-content-porting-discipline.md §3 + session evaluation
|
|
123
|
+
* 2026-08-16 discrimination contract).
|
|
124
|
+
*
|
|
125
|
+
* Discrimination (HARD — zero false positives on the skills corpus):
|
|
126
|
+
* - `task-<digits>-(brief|report|fix-report|diff)` with 1+ digits is a
|
|
127
|
+
* concrete instance → reported (`task-2-report`, `task-1.diff`).
|
|
128
|
+
* Placeholders (`task-N-brief`, `task-N-report`, `<plan-id>`,
|
|
129
|
+
* `{SDD_DIR}/task-N-report.md`) never match.
|
|
130
|
+
* - `.mstar/sdd/<segment>` / `.agents/sdd/<segment>` with a concrete first
|
|
131
|
+
* segment (`20260815-x`) → reported; `<plan-id>` / `{SDD_DIR}` segments
|
|
132
|
+
* are template forms → never match.
|
|
133
|
+
*
|
|
134
|
+
* Discovery only — a finder returning an array, same shape as
|
|
135
|
+
* `findSimplifyMarkers`, NOT a GateResult; callers wrap findings into
|
|
136
|
+
* `ViolationResult`s (codes `skill.ephemeral.task-artifact` /
|
|
137
|
+
* `skill.ephemeral.sdd-deeplink`). Citations are reported line by line in
|
|
138
|
+
* 1-based line order, source order within a line.
|
|
139
|
+
*/
|
|
140
|
+
export declare function findEphemeralCitations(skillText: string): EphemeralCitation[];
|
|
98
141
|
/**
|
|
99
142
|
* Assert the SDD TDD triple is present in a `task-N-report.md` text
|
|
100
143
|
* (mstar-coding-behavior § Integration Notes — "completion evidence must
|
|
@@ -36,19 +36,43 @@ export type FiveQuestionSection = {
|
|
|
36
36
|
question: string;
|
|
37
37
|
};
|
|
38
38
|
export declare const FIVE_QUESTION_SECTIONS: readonly FiveQuestionSection[];
|
|
39
|
+
/** Strip a leading `---`-fenced YAML frontmatter block, returning the body
|
|
40
|
+
* (the five-question lint takes the body; the frontmatter lint takes the
|
|
41
|
+
* full doc). Returns the input unchanged when there is no closing fence —
|
|
42
|
+
* an unparseable block is treated as body rather than destroyed. Canonical
|
|
43
|
+
* helper shared by the CLI, drift-lint Guard 5 and the engine corpus test. */
|
|
44
|
+
export declare function stripFrontmatter(text: string): string;
|
|
45
|
+
/** Lint mode: `authoring` (canonical headings only — greenfield / strict)
|
|
46
|
+
* or `runtime` (canonical plus the locked `RUNTIME_HEADING_ALIASES` table
|
|
47
|
+
* — shipped `mstar-*` topic skills). */
|
|
48
|
+
export type FiveQuestionMode = "authoring" | "runtime";
|
|
49
|
+
/**
|
|
50
|
+
* Locked runtime alias table (plan 20260816-audit-001-five-question-runtime-
|
|
51
|
+
* alignment, Step 2): heading synonyms that answer the same question for
|
|
52
|
+
* shipped topic skills, verified against the corpus at `81480e7`. Tokens are
|
|
53
|
+
* case-insensitive heading substrings (any heading level); the `decision-rules`
|
|
54
|
+
* breadth is bounded by the corpus regression test pinning current state.
|
|
55
|
+
* `load-order` has no aliases — the canonical label covers 15/16 skills and
|
|
56
|
+
* `mstar-host` closes its gap with a corpus edit instead. Chinese tokens are
|
|
57
|
+
* `\uXXXX` escapes (ASCII-only src literals; runtime value is identical).
|
|
58
|
+
*/
|
|
59
|
+
export declare const RUNTIME_HEADING_ALIASES: Readonly<Record<string, readonly string[]>>;
|
|
39
60
|
/**
|
|
40
61
|
* Lint a SKILL.md body for the 5-question contract (mstar-skill-authoring
|
|
41
62
|
* § Body 必须回答的 5 问). Heuristic: each of the five questions must be
|
|
42
63
|
* answered by the presence of its canonical section heading (case-
|
|
43
64
|
* insensitive substring match on heading text, any heading level — so
|
|
44
65
|
* "## Load Order (Required)" and "### Workflow — main path" both match).
|
|
45
|
-
*
|
|
46
|
-
*
|
|
66
|
+
* `mode: "runtime"` additionally accepts the locked `RUNTIME_HEADING_ALIASES`
|
|
67
|
+
* synonyms for shipped topic skills; `authoring` (default) stays canonical-
|
|
68
|
+
* only — greenfield skills still must use the canonical headings. Content
|
|
69
|
+
* judgment (whether the answer is actually narrow / procedural) stays prompt.
|
|
70
|
+
* Advisory: violations are `low` severity (v1 non-blocking).
|
|
47
71
|
*
|
|
48
72
|
* Violations: `skill-authoring.five-question.<key>` for each uncovered
|
|
49
73
|
* question.
|
|
50
74
|
*/
|
|
51
|
-
export declare function lintFiveQuestion(bodyText: string): GateResult;
|
|
75
|
+
export declare function lintFiveQuestion(bodyText: string, mode?: FiveQuestionMode): GateResult;
|
|
52
76
|
/**
|
|
53
77
|
* Resolve a skill-relative asset path (mstar-skill-authoring § Skill-
|
|
54
78
|
* relative script and asset paths: name assets as skill `<name>` →
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mstar-harness/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Morning Star Harness Workflow Engine — deterministic workflow enforcement library (path, status, lease, dispatch, sdd, iteration, lint gates).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|