@mstar-harness/engine 0.0.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/README.md +27 -0
- package/dist/audit.d.ts +99 -0
- package/dist/compound.d.ts +90 -0
- package/dist/core.d.ts +112 -0
- package/dist/design-md.d.ts +113 -0
- package/dist/dispatch.d.ts +205 -0
- package/dist/engine.js +3439 -0
- package/dist/host.d.ts +95 -0
- package/dist/index.d.ts +52 -0
- package/dist/iteration.d.ts +88 -0
- package/dist/lease.d.ts +193 -0
- package/dist/lint.d.ts +206 -0
- package/dist/path.d.ts +117 -0
- package/dist/roles.d.ts +87 -0
- package/dist/sdd.d.ts +123 -0
- package/dist/skill-authoring.d.ts +60 -0
- package/dist/status.d.ts +168 -0
- package/dist/worktree.d.ts +104 -0
- package/package.json +41 -0
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine host module — host auto-detection from tool shapes, skill-root
|
|
3
|
+
* resolution, and the type-only HostAdapter contract (thin; roadmap §8.2
|
|
4
|
+
* `host` row, §4.2/§8.4).
|
|
5
|
+
*
|
|
6
|
+
* Source skills (semantic SSOT — this module ports their deterministic
|
|
7
|
+
* rules verbatim, it never redefines them; roadmap §8.5 C2):
|
|
8
|
+
* - `mstar-host` SKILL.md § Detect active host — the ordered table
|
|
9
|
+
* cursor → opencode → omp → kimi → zcode → codex ("Order matters").
|
|
10
|
+
* - `mstar-host` SKILL.md § Resolve loaded skill root — per-host skill-root
|
|
11
|
+
* resolution.
|
|
12
|
+
* - `.harness/references/skill-programmatic-roadmap.md` §8.4 — the
|
|
13
|
+
* `HostAdapter` shared contract (all hooks optional; no concrete adapters
|
|
14
|
+
* in the engine; pi/dsh deferred).
|
|
15
|
+
*
|
|
16
|
+
* UX judgment (ambiguous-host fallback reasoning, plan-mode bridges) stays
|
|
17
|
+
* prompt — this module only turns tool shapes into a host id.
|
|
18
|
+
*/
|
|
19
|
+
import type { GateResult, ValidationResult } from "./core.js";
|
|
20
|
+
import type { AssignmentFields } from "./dispatch.js";
|
|
21
|
+
import type { IntegrationMergeLease } from "./lease.js";
|
|
22
|
+
/** All hosts the engine knows about (roadmap §8.4 host union). `pi` and
|
|
23
|
+
* `dsh` have no plugin API in v1 — they appear in the union (and in
|
|
24
|
+
* `HostAdapter.host`) but are never detected and get no adapters. */
|
|
25
|
+
export type HostId = "opencode" | "omp" | "pi" | "dsh" | "cursor" | "codex" | "kimi" | "zcode";
|
|
26
|
+
/** Result of `detectHost`: one of the six known hosts or `ambiguous`
|
|
27
|
+
* (prompt judgment then applies per mstar-host). */
|
|
28
|
+
export type DetectResult = "opencode" | "omp" | "cursor" | "codex" | "kimi" | "zcode" | "ambiguous";
|
|
29
|
+
/** Tool-shape signal tokens accepted by `detectHost`, derived from the
|
|
30
|
+
* mstar-host detection table. Plan-mode extras (CreatePlan/SwitchMode) and
|
|
31
|
+
* Browser-plugin tools are documented in the table but are not part of the
|
|
32
|
+
* v1 signal enum. */
|
|
33
|
+
export type ToolSignal = "subagent_type" | "question" | "task_subagent" | "task_agent_batch" | "ask" | "hub" | "Agent" | "AgentSwarm" | "AskUserQuestion" | "EnterPlanMode" | "TodoWrite" | "plan_slash" | "goal" | "functions.*" | "tool_search";
|
|
34
|
+
/**
|
|
35
|
+
* Detect the active host from session tool shapes, per the ordered
|
|
36
|
+
* mstar-host table (ported verbatim):
|
|
37
|
+
*
|
|
38
|
+
* | Signal | Host |
|
|
39
|
+
* |--------|------|
|
|
40
|
+
* | `subagent_type` (Task param; plan mode + CreatePlan/SwitchMode) | cursor |
|
|
41
|
+
* | `question`, or `task_subagent` (task tool, singular subagent, no batch) | opencode |
|
|
42
|
+
* | `task_agent_batch` (task tool, agent/tasks[] batch), `ask`, `hub` | omp |
|
|
43
|
+
* | `Agent`/`AskUserQuestion`/`EnterPlanMode` + `AgentSwarm` (Kimi-only) | kimi |
|
|
44
|
+
* | `Agent`/`AskUserQuestion`/`EnterPlanMode`/`TodoWrite`, no `AgentSwarm` | zcode |
|
|
45
|
+
* | `/plan`, `/goal`; Goal tools; `functions.*` namespaces; `tool_search` | codex |
|
|
46
|
+
*
|
|
47
|
+
* Order matters: cursor → opencode → omp → kimi → zcode → codex — the
|
|
48
|
+
* sharpest Task-based split is `subagent_type` (Cursor) vs `subagent`
|
|
49
|
+
* (OpenCode) vs `agent`/`tasks[]` (omp). Still ambiguous → `"ambiguous"`
|
|
50
|
+
* (prompt judgment stays in the skill).
|
|
51
|
+
*/
|
|
52
|
+
export declare function detectHost(signals: readonly ToolSignal[]): DetectResult;
|
|
53
|
+
/** Skill name + optional skill-relative path for skill-root resolution. */
|
|
54
|
+
export type SkillRootPaths = {
|
|
55
|
+
skill: string;
|
|
56
|
+
rel?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the loaded skill root for a host (mstar-host § Resolve loaded
|
|
60
|
+
* skill root). Returns the canonical resolution string for the host —
|
|
61
|
+
* resolve the loaded skill directory first, never `skills/<name>/…` from a
|
|
62
|
+
* consumer app cwd (that layout exists in the harness source / plugin
|
|
63
|
+
* package only).
|
|
64
|
+
*
|
|
65
|
+
* | Host | Resolution |
|
|
66
|
+
* |------|------------|
|
|
67
|
+
* | omp | `skill://<name>[/<rel>]` (filesystem fallback: plugin package root `skills/<name>/` after install/link) |
|
|
68
|
+
* | cursor | `~/.cursor/plugins/local/morning-star-harness/skills/<name>[/<rel>]` (global plugin fallback; prefer skill name via plugin skills) |
|
|
69
|
+
* | codex | `skills/<name>[/<rel>]` (plugin-mounted; project command skills under `.agents/skills/<name>/`) |
|
|
70
|
+
* | opencode | `harness-skills/<name>[/<rel>]` (package-internal via `@mstar-harness/opencode` — never `process.cwd()/skills/`) |
|
|
71
|
+
* | kimi / zcode | `./skills/<name>[/<rel>]` (plugin mount from the installed plugin root) |
|
|
72
|
+
* | pi / dsh | deferred — no plugin API in v1 (roadmap §8.4) |
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveSkillRoot(host: HostId, paths: SkillRootPaths): string;
|
|
75
|
+
/**
|
|
76
|
+
* Shared host-adapter contract (roadmap §8.4) — every host plugin implements
|
|
77
|
+
* this; the engine never imports host-specific SDKs. All lifecycle hooks are
|
|
78
|
+
* optional so a host degrades gracefully when a slot is absent (standalone
|
|
79
|
+
* rule: skill text remains authoritative; the engine only returns results
|
|
80
|
+
* the caller chooses to honor). `log` is required — adapters must be able to
|
|
81
|
+
* report. No concrete adapters ship in the engine: opencode binds via its
|
|
82
|
+
* own plugin code, omp via the command layer; pi/dsh adapters are deferred
|
|
83
|
+
* until their plugin APIs land.
|
|
84
|
+
*/
|
|
85
|
+
export interface HostAdapter {
|
|
86
|
+
host: "opencode" | "omp" | "pi" | "dsh" | "cursor" | "codex" | "kimi" | "zcode";
|
|
87
|
+
/** Called before a status.json write; return a validation result the host
|
|
88
|
+
* surfaces (non-blocking warn in v1, opt-in block in v2). */
|
|
89
|
+
beforeStatusWrite?: (path: string, doc: unknown) => Promise<ValidationResult>;
|
|
90
|
+
/** Called before a subagent dispatch; return the Assignment field gate. */
|
|
91
|
+
beforeDispatch?: (assignment: AssignmentFields) => Promise<GateResult>;
|
|
92
|
+
/** Called before an integration-branch merge; return the lease gate. */
|
|
93
|
+
beforeMerge?: (lease: IntegrationMergeLease) => Promise<GateResult>;
|
|
94
|
+
log: (level: "info" | "warn" | "error", msg: string) => void;
|
|
95
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mstar-harness/engine — public entry (exports map `.` → `dist/engine.js`).
|
|
3
|
+
*
|
|
4
|
+
* Engine = importable library for deterministic harness checks; the CLI and
|
|
5
|
+
* OpenCode plugin consume it in-process. `core` is the shared type/version
|
|
6
|
+
* base, `path` implements harness path resolution + scaffold + gitignore
|
|
7
|
+
* checks, `status` implements the status.json schema, residual lifecycle,
|
|
8
|
+
* findings-cleanup gate and the tech-debt rollup port, `lease` implements
|
|
9
|
+
* the execution/merge lease state machines + same-host status write lock,
|
|
10
|
+
* `dispatch` implements the Assignment field contract, default-branch
|
|
11
|
+
* gate, QC seat mapping and tri-identity/anti-recursion prechecks, `lint`
|
|
12
|
+
* implements marker/TDD-triple/plan-quality/frontmatter/STRATEGY checks,
|
|
13
|
+
* `design-md` validates DESIGN.md token frontmatter + light/dark parity +
|
|
14
|
+
* completeness levels, `audit` validates audit Status blocks, redacts
|
|
15
|
+
* secrets and scaffolds audit-<date>/ plan dirs, and `compound` validates
|
|
16
|
+
* knowledge-doc schema, reference existence, index rows and the
|
|
17
|
+
* compound-refresh scope. `roles` validates the role reference mapping +
|
|
18
|
+
* parameter tables and the load-order contract, `host` detects the active
|
|
19
|
+
* host from tool shapes, resolves skill roots and defines the type-only
|
|
20
|
+
* `HostAdapter` contract, and `skill-authoring` lints frontmatter +
|
|
21
|
+
* 5-question bodies and resolves skill-relative asset paths.
|
|
22
|
+
*/
|
|
23
|
+
export type { GateResult, Severity, ValidationResult } from "./core.js";
|
|
24
|
+
export { SEVERITY_ORDER, applyEnforcement, readHarnessVersion, readJson, resolveProjectRoot, writeJson } from "./core.js";
|
|
25
|
+
export type { HarnessKind, ResolveHarnessDirOptions, ResolveSpecsDirOptions } from "./path.js";
|
|
26
|
+
export { assertPlanWritingPath, emitGitignoreSnippet, resolveHarnessDir, resolveIterationDir, resolvePlanDir, resolveSddDir, resolveSpecsDir, scaffoldHarness, validateGitignore, } from "./path.js";
|
|
27
|
+
export type { ArchiveResult, FindingsCleanupMode, PlanRow, ResidualEntry, StatusDoc, TechDebtCheck, TechDebtRollup, TechDebtSummary, } from "./status.js";
|
|
28
|
+
export { archiveResiduals, findingsCleanupGate, normalizeSeverity, resolveCompassEnforcement, techDebtRollup, validatePlanRow, validateResidual, validateStatus, } from "./status.js";
|
|
29
|
+
export type { ClaimLeaseFields, ExecutionLease, ExecutionLeaseLocations, IntegrationMergeLease, LeaseTransition, LeaseVerifyResult, } from "./lease.js";
|
|
30
|
+
export { canSteal, claimLease, planExecutionLeaseLocations, releaseLease, sameHolderResume, validateExecutionLease, validateIntegrationMergeLease, verifyPlanExecutionLease, withStatusWriteLock, } from "./lease.js";
|
|
31
|
+
export type { AssignmentBranchForms, AssignmentFields, DefaultBranchOptions, EnforcementFlag, EnforcementSource, ExecutionModeToNOptions, ExecutionModeToNResult, ValidateAssignmentFieldsOptions, } from "./dispatch.js";
|
|
32
|
+
export { antiRecursionPrecheck, assertDefaultBranchProtected, assertTriIdentity, assignmentHeaderRegion, executionModeToN, isReadOnlyAssignmentRole, parseAssignmentBranchForms, parseAssignmentFields, parseBranchPolicyDirectOnBranch, parseEnforcementFlag, validateAssignmentFields, } from "./dispatch.js";
|
|
33
|
+
export type { BranchProbeOptions, L1PreDispatchInput, L2PreDispatchInput, QcAlignmentAssignment, QcSnapshotAssignment, WorktreeTrack, } from "./worktree.js";
|
|
34
|
+
export { assertBranchAlignment, assertControlVsFeaturePath, assertQcAlignment, l1PreDispatchCheck, l2PreDispatchCheck, singleReviewSnapshot, } from "./worktree.js";
|
|
35
|
+
export type { ImplementerSessionLedger, ReviewPackageOptions, SddWorkspaceOptions, StickyRulesInput, StickyRulesResult, TaskBriefOptions, } from "./sdd.js";
|
|
36
|
+
export { SddScriptError, assertBaseSha, implementerSessionStickyRules, readProgressLedger, reviewPackage, sddWorkspace, taskBrief, taskReportExists, } from "./sdd.js";
|
|
37
|
+
export type { CompassDoc, PhaseGateOptions, PhaseGateResult, PhaseTransition, } from "./iteration.js";
|
|
38
|
+
export { assertIndexRowObligations, evaluatePhaseGate, pushCadenceProbe, validateCompassFrontmatter, } from "./iteration.js";
|
|
39
|
+
export type { CompletenessItem, CompletenessLevel, CompletenessPlaceholder, CompletenessResult, DesignFrontmatter, } from "./design-md.js";
|
|
40
|
+
export { assertLightDarkParity, completenessLevel, parseDesignFrontmatter, validateDesignTokenFrontmatter, } from "./design-md.js";
|
|
41
|
+
export type { AuditCategory, AuditEffort, AuditFinding, AuditPriority, AuditRisk, RedactResult, ScaffoldAuditPlanOptions, ScaffoldAuditPlanResult, SecretFinding, } from "./audit.js";
|
|
42
|
+
export { AUDIT_CATEGORIES, AUDIT_EFFORTS, AUDIT_PRIORITIES, AUDIT_RISKS, redactSecrets, scaffoldAuditPlan, validateAuditStatusBlocks, } from "./audit.js";
|
|
43
|
+
export type { ReferenceCheckResult } from "./compound.js";
|
|
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";
|
|
47
|
+
export type { DevTrackParam, QcReviewerParam, RoleFamily, RoleMappingEntry, RoleMappingOptions, } from "./roles.js";
|
|
48
|
+
export { DEV_TRACK_PARAMS, QC_REVIEWER_PARAMS, ROLE_MAPPING, SHARED_FAMILIES, lintLoadOrder, validateRoleMapping, } from "./roles.js";
|
|
49
|
+
export type { DetectResult, HostAdapter, HostId, SkillRootPaths, ToolSignal } from "./host.js";
|
|
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";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { GateResult, ValidationResult } from "./core.js";
|
|
2
|
+
import { type StatusDoc } from "./status.js";
|
|
3
|
+
/**
|
|
4
|
+
* Loose shape of a parsed delivery-compass.md frontmatter. All fields are
|
|
5
|
+
* `unknown` because documents come from YAML at runtime; validators narrow
|
|
6
|
+
* them.
|
|
7
|
+
*/
|
|
8
|
+
export type CompassDoc = {
|
|
9
|
+
iteration_id?: unknown;
|
|
10
|
+
start_date?: unknown;
|
|
11
|
+
end_date?: unknown;
|
|
12
|
+
status?: unknown;
|
|
13
|
+
iteration_base_branch?: unknown;
|
|
14
|
+
target_branch?: unknown;
|
|
15
|
+
plans?: unknown;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
/** Where the iteration stands per the Phase transition gates table. */
|
|
19
|
+
export type PhaseTransition = "phase-2-execute" | "phase-3-close" | "phase-4-pr-delivery";
|
|
20
|
+
/**
|
|
21
|
+
* Git probe inputs for the §3.5 exit checklist. The engine never shells out
|
|
22
|
+
* to git; callers (CLI / host hooks) probe and pass values in.
|
|
23
|
+
*/
|
|
24
|
+
export type PhaseGateOptions = {
|
|
25
|
+
/** `git branch --show-current` of the working checkout (Phase 3 runs on the integration branch). */
|
|
26
|
+
currentBranch?: string;
|
|
27
|
+
/** Expected `spec_integration_branch` (status.json metadata / compass Delivery Branch Policy). */
|
|
28
|
+
specIntegrationBranch?: string;
|
|
29
|
+
/** Resolved PR base branch for Phase 4 (§3.5 exit item 6). */
|
|
30
|
+
prBaseBranch?: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Result of the phase-transition gate evaluation. `entry`/`exit` are the
|
|
34
|
+
* checkable subsets of the §3.1 / §3.5 checklists; `ok`/`violations` are the
|
|
35
|
+
* gate verdict over the triggered transition:
|
|
36
|
+
* - not all plans Done → `phase-2-execute` (keep executing; gate passes).
|
|
37
|
+
* - all plans Done with missing checklist items → `phase-3-close` (Phase 3
|
|
38
|
+
* required; missing items listed in `violations`).
|
|
39
|
+
* - all plans Done and both checklists clean → `phase-4-pr-delivery`.
|
|
40
|
+
*
|
|
41
|
+
* Note (qc2 F-003): during the Phase-3 window `ok` is false because the
|
|
42
|
+
* §3.4 close items (`status: completed` + `end_date`) are only written at
|
|
43
|
+
* the END of close — the exit checklist gates Phase 4, not the Phase-3
|
|
44
|
+
* entry, so callers (e.g. the CLI, which exits 1) must treat that as "close
|
|
45
|
+
* work pending", not "don't enter Phase 3".
|
|
46
|
+
*/
|
|
47
|
+
export type PhaseGateResult = {
|
|
48
|
+
transition: PhaseTransition;
|
|
49
|
+
allPlansDone: boolean;
|
|
50
|
+
/** §3.1 close entry checklist — checkable subset (HARD GATE before §3.2). */
|
|
51
|
+
entry: GateResult;
|
|
52
|
+
/** §3.5 close exit checklist — checkable subset (gate to Phase 4). */
|
|
53
|
+
exit: GateResult;
|
|
54
|
+
ok: boolean;
|
|
55
|
+
violations: ValidationResult[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Validate a parsed delivery-compass.md frontmatter (mstar-iteration §1.3 +
|
|
59
|
+
* iteration-compass-template.md Fields guide). `end_date` is required only
|
|
60
|
+
* when `status: completed` (Phase 3 §3.4) and is a violation while the
|
|
61
|
+
* iteration is still `active`/`locked`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateCompassFrontmatter(doc: unknown): GateResult;
|
|
64
|
+
/**
|
|
65
|
+
* Evaluate the Phase transition gates (mstar-iteration Phase transition
|
|
66
|
+
* gates table): all compass-registered plans `Done` (per statusDoc plans[]
|
|
67
|
+
* status) → Phase 3 required, with the checkable subsets of the §3.1 entry
|
|
68
|
+
* and §3.5 exit checklists as missing-item violations.
|
|
69
|
+
*
|
|
70
|
+
* Pure function — git probes (`currentBranch`, `specIntegrationBranch`,
|
|
71
|
+
* `prBaseBranch`) come from the caller via `opts`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function evaluatePhaseGate(statusDoc: StatusDoc, compassDoc: CompassDoc, opts?: PhaseGateOptions): PhaseGateResult;
|
|
74
|
+
/**
|
|
75
|
+
* §5.1a push-cadence probe (HARD): never push the PR head while required CI
|
|
76
|
+
* is still queued/in_progress or an AI/bot review wave is running. Pure
|
|
77
|
+
* function — no external calls; callers probe CI / review state and pass the
|
|
78
|
+
* booleans. Fix locally early, push once the wave settles (§5.1a push gate
|
|
79
|
+
* 1 + 2).
|
|
80
|
+
*/
|
|
81
|
+
export declare function pushCadenceProbe(ciRunning: boolean, reviewWaveActive: boolean): GateResult;
|
|
82
|
+
/**
|
|
83
|
+
* §1.4 index obligations: one row per iteration in `{ITERATION_DIR}/README.md`
|
|
84
|
+
* (table header on first creation). Iterations are discovered as subdirectories
|
|
85
|
+
* of `iterationsDir` containing `delivery-compass.md`. Returns violations for
|
|
86
|
+
* a missing README, a missing header, and missing per-iteration rows.
|
|
87
|
+
*/
|
|
88
|
+
export declare function assertIndexRowObligations(iterationsDir: string): GateResult;
|
package/dist/lease.d.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { GateResult, ValidationResult } from "./core.js";
|
|
2
|
+
import type { PlanRow } from "./status.js";
|
|
3
|
+
/**
|
|
4
|
+
* `plans[].execution_lease` (v1) — see spec header. Extra fields (e.g. the
|
|
5
|
+
* real control data's `base_sha`) are allowed and preserved.
|
|
6
|
+
*/
|
|
7
|
+
export type ExecutionLease = {
|
|
8
|
+
holder: string;
|
|
9
|
+
claimed_at: string;
|
|
10
|
+
worktree_path: string;
|
|
11
|
+
working_branch: string;
|
|
12
|
+
session_label?: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Root `metadata.integration_merge_lease` (v1) — see spec header. Absent =
|
|
17
|
+
* unclaimed; writers delete the key on release (never `null`/tombstone).
|
|
18
|
+
*/
|
|
19
|
+
export type IntegrationMergeLease = {
|
|
20
|
+
holder: string;
|
|
21
|
+
claimed_at: string;
|
|
22
|
+
plan_id: string;
|
|
23
|
+
source_branch: string;
|
|
24
|
+
target_branch: string;
|
|
25
|
+
session_label?: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
/** Assignment-side fields written into an `execution_lease` at claim time. */
|
|
29
|
+
export type ClaimLeaseFields = {
|
|
30
|
+
worktree_path: string;
|
|
31
|
+
working_branch: string;
|
|
32
|
+
session_label?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Result of a pure lease transition on one plan row. `row` is the resulting
|
|
36
|
+
* row (unchanged when `ok` is false); `outcome` distinguishes a fresh claim
|
|
37
|
+
* from a same-holder resume / a release.
|
|
38
|
+
*/
|
|
39
|
+
export type LeaseTransition = {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
row: PlanRow;
|
|
42
|
+
outcome?: "claimed" | "resumed" | "released";
|
|
43
|
+
violations: ValidationResult[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Validate one `plans[].execution_lease` object (status-and-residuals.md
|
|
47
|
+
* § `plans[].execution_lease`): required `holder` / `claimed_at` /
|
|
48
|
+
* `worktree_path` (absolute) / `working_branch`; optional `session_label`
|
|
49
|
+
* display-only. `null` and tombstone objects are invalid — writers delete
|
|
50
|
+
* the key on release, never write `null` (§ Agent prohibitions). Extra
|
|
51
|
+
* fields are allowed (the real control lease carries `base_sha`).
|
|
52
|
+
*/
|
|
53
|
+
export declare function validateExecutionLease(lease: unknown): GateResult;
|
|
54
|
+
/**
|
|
55
|
+
* Validate one root `metadata.integration_merge_lease` object
|
|
56
|
+
* (status-and-residuals.md § Root `metadata.integration_merge_lease` (v1)):
|
|
57
|
+
* required `holder` / `claimed_at` / `plan_id` / `source_branch` /
|
|
58
|
+
* `target_branch`; optional `session_label`. Absent = unclaimed; `null` and
|
|
59
|
+
* tombstone objects are invalid (writers delete the key on release).
|
|
60
|
+
* Integration merges into `spec_integration_branch` are serial — one holder
|
|
61
|
+
* at a time (phase-2-worktree-lease.md § Integration merge lease).
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateIntegrationMergeLease(lease: unknown): GateResult;
|
|
64
|
+
/**
|
|
65
|
+
* Claim-before-`InProgress` transition (status-and-residuals.md
|
|
66
|
+
* § Claim-before-`InProgress`; phase-2-worktree-lease.md § Execution lease).
|
|
67
|
+
*
|
|
68
|
+
* - No lease + status `Todo`/`Blocked` → claim: status becomes `InProgress`
|
|
69
|
+
* and the full `execution_lease` is written in one update (`claimed_at`
|
|
70
|
+
* = now, RFC 3339 UTC with `Z`).
|
|
71
|
+
* - Lease with the **same** `holder` → resume: the stored lease is preserved
|
|
72
|
+
* verbatim, but `worktree_path`/`working_branch` must match the Assignment
|
|
73
|
+
* (`verify-held-lease`) or the resume is refused.
|
|
74
|
+
* - Lease with a **different** `holder` → refused: no timestamp makes it
|
|
75
|
+
* stealable.
|
|
76
|
+
* - `InProgress` **without** a lease → orphan: refused; never invent a
|
|
77
|
+
* lease (§ Orphan recovery — recovery is PM/human-owned).
|
|
78
|
+
* - `null`/tombstone stored lease → refused, not silently replaced
|
|
79
|
+
* (§ Agent prohibitions).
|
|
80
|
+
* - The fields to be written are validated via `validateExecutionLease`
|
|
81
|
+
* **before** the Todo/Blocked → InProgress transition commits: a relative
|
|
82
|
+
* `worktree_path` or missing fields are rejected without mutating the row
|
|
83
|
+
* (the written lease must itself pass the validator).
|
|
84
|
+
*
|
|
85
|
+
* Pure: returns the resulting row; the caller performs the locked
|
|
86
|
+
* read-check-replace-verify around `status.json` and persists it.
|
|
87
|
+
*/
|
|
88
|
+
export declare function claimLease(row: PlanRow, holder: string, fields: ClaimLeaseFields): LeaseTransition;
|
|
89
|
+
/**
|
|
90
|
+
* Release transition — deletes `execution_lease` entirely, never writes
|
|
91
|
+
* `null` (status-and-residuals.md § "Hold, release, and override" + § Agent
|
|
92
|
+
* prohibitions). Requires the **same-session holder**: when `holder` differs
|
|
93
|
+
* from the stored lease `holder`, the release is refused and the row is left
|
|
94
|
+
* unmodified — a different holder must Blocked, never released by another
|
|
95
|
+
* session (no timestamp makes a lease stealable). Idempotent when no lease
|
|
96
|
+
* is present. The `Done` authority deletes the lease in the same
|
|
97
|
+
* complete-file update as `status: "Done"` — **only after** successful
|
|
98
|
+
* integration merge into `spec_integration_branch` (§ Integration merge
|
|
99
|
+
* protocol); the caller enforces that ordering around the locked update.
|
|
100
|
+
*/
|
|
101
|
+
export declare function releaseLease(row: PlanRow, holder: string): LeaseTransition;
|
|
102
|
+
/**
|
|
103
|
+
* Same-holder resume check (status-and-residuals.md § Claim-before-`InProgress`
|
|
104
|
+
* #2): `true` iff the stored lease `holder` equals the session `holder`.
|
|
105
|
+
* Verify-held-lease (worktree_path/working_branch vs the Assignment) is the
|
|
106
|
+
* caller's check via `claimLease`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function sameHolderResume(lease: unknown, holder: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Steal decision (status-and-residuals.md § Agent prohibitions + § "Hold,
|
|
111
|
+
* release, and override"): an active lease MUST NOT be stolen — `false`
|
|
112
|
+
* unless the **current-turn user explicitly authorizes** the override
|
|
113
|
+
* (`opts.userOverride: true`). The caller MUST still append an audit entry
|
|
114
|
+
* to `plans[].notes` (timestamp, prior holder, new holder/release, user
|
|
115
|
+
* authorized). Agents MUST NOT infer override from age, inactivity, `Blocked`
|
|
116
|
+
* status, or a failed session.
|
|
117
|
+
*/
|
|
118
|
+
export declare function canSteal(lease: unknown, holder: string, opts?: {
|
|
119
|
+
userOverride?: boolean;
|
|
120
|
+
}): boolean;
|
|
121
|
+
/** Execution lease locations found on one plan row (SSOT + legacy read-compat). */
|
|
122
|
+
export type ExecutionLeaseLocations = {
|
|
123
|
+
/** SSOT location: `plans[].execution_lease`. */
|
|
124
|
+
row: unknown;
|
|
125
|
+
/** Legacy/hand-written read-compat location: `plans[].metadata.execution_lease`. */
|
|
126
|
+
metadata: unknown;
|
|
127
|
+
};
|
|
128
|
+
export declare function planExecutionLeaseLocations(row: Record<string, unknown>): ExecutionLeaseLocations;
|
|
129
|
+
export type LeaseVerifyResult = {
|
|
130
|
+
ok: boolean;
|
|
131
|
+
violations: ValidationResult[];
|
|
132
|
+
/** The lease chosen for validation (row-level wins) — absent when neither location has one. */
|
|
133
|
+
lease?: unknown;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Verify a plan's `execution_lease` across its two possible locations
|
|
137
|
+
* (status-and-residuals.md § `plans[].execution_lease`; ADR
|
|
138
|
+
* 2026-07-22-iteration-worktree-plan-lease.md A3 — the plan row is the
|
|
139
|
+
* claim/hold/release SSOT):
|
|
140
|
+
* - Row-level `plans[].execution_lease` only, valid → OK.
|
|
141
|
+
* - Metadata-only (`plans[].metadata.execution_lease`) → high-severity
|
|
142
|
+
* `lease.verify.non-ssot-location`: the metadata location is a
|
|
143
|
+
* legacy/hand-written read-compat fallback, NOT equivalent to SSOT
|
|
144
|
+
* success. Always a FAIL (non-zero exit) with the lease shape still
|
|
145
|
+
* validated and reported.
|
|
146
|
+
* - Both locations present → `lease.verify.dual-write`: the row-level lease
|
|
147
|
+
* wins and is validated; the metadata copy must be deleted.
|
|
148
|
+
* - Neither present → `lease.verify.missing` (non-InProgress) /
|
|
149
|
+
* `lease.verify.orphan` (InProgress).
|
|
150
|
+
*
|
|
151
|
+
* Kept in the engine so every host hook / CLI entry / Slice-2+ consumer
|
|
152
|
+
* imports ONE gate (CLI `mstar lease verify` is a thin wrapper).
|
|
153
|
+
*/
|
|
154
|
+
export declare function verifyPlanExecutionLease(row: Record<string, unknown>, planId: string): LeaseVerifyResult;
|
|
155
|
+
/**
|
|
156
|
+
* Same-host exclusive write lock around `status.json` coordination writes
|
|
157
|
+
* (status-and-residuals.md § "Same-host exclusive write lock (control
|
|
158
|
+
* status.json)"; phase-2-worktree-lease.md § "Same-host exclusive write
|
|
159
|
+
* lock"). Lease mutations and plan-status transitions that touch leases MUST
|
|
160
|
+
* run inside this lock for the full read-check-replace-verify sequence.
|
|
161
|
+
*
|
|
162
|
+
* Acquires by atomic `mkdir` on `<status dir>/.status-write.lockdir/`
|
|
163
|
+
* (success acquires; existing dir → another writer holds the lock). While
|
|
164
|
+
* another writer holds it, wait up to `timeoutMs` (default 30s) and then
|
|
165
|
+
* throw (Blocked) — the lockdir is never removed for another holder.
|
|
166
|
+
*
|
|
167
|
+
* Ownership guard (double-unlock safety): the lockdir's `(dev, ino)` is
|
|
168
|
+
* captured at acquisition; `finally` re-stats the path and removes the
|
|
169
|
+
* directory ONLY when the identity is unchanged. When `fn` itself removed
|
|
170
|
+
* the lockdir (e.g. explicit rollback), or another writer replaced it with
|
|
171
|
+
* a fresh lockdir before this writer's `finally` ran, the removal is
|
|
172
|
+
* skipped — a second writer's lock is never destroyed.
|
|
173
|
+
*
|
|
174
|
+
* Reentrancy: a nested acquisition on the same lockdir within the same
|
|
175
|
+
* async context (i.e. `fn` calling `withStatusWriteLock` on the same
|
|
176
|
+
* status.json) throws immediately instead of waiting out the timeout.
|
|
177
|
+
*
|
|
178
|
+
* Crash diagnosis: a `holder.pid` file (acquiring process id) is written
|
|
179
|
+
* inside the lockdir on acquisition and removed on release. A hard crash
|
|
180
|
+
* between `mkdirSync` and release leaks the lockdir; the timeout error
|
|
181
|
+
* message names the recovery step (remove the lockdir when no writer is
|
|
182
|
+
* alive).
|
|
183
|
+
*
|
|
184
|
+
* simplify: mkdir lockdir is the SSOT-documented alternative to `flock`
|
|
185
|
+
* (`{HARNESS_DIR}/.status-write.lock`) — Bun 1.2 exposes no `node:fs`
|
|
186
|
+
* flock/flockSync, so the advisory-file variant is unavailable here. Unlike
|
|
187
|
+
* flock, a hard process crash leaks the lockdir; swap to flock when the
|
|
188
|
+
* runtime provides it.
|
|
189
|
+
*/
|
|
190
|
+
export declare function withStatusWriteLock<T>(statusPath: string, fn: () => T | Promise<T>, opts?: {
|
|
191
|
+
timeoutMs?: number;
|
|
192
|
+
pollMs?: number;
|
|
193
|
+
}): Promise<T>;
|
package/dist/lint.d.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine lint module — deterministic lint checks ported from skill prose.
|
|
3
|
+
*
|
|
4
|
+
* Roadmap: `.harness/references/skill-programmatic-roadmap.md` §8.2 `lint`
|
|
5
|
+
* row + §4.5 Lint layer (`simplify:` / `temporary` marker presence; SDD TDD
|
|
6
|
+
* triple in completion reports; plan-quality-bar checks; skill frontmatter
|
|
7
|
+
* contract; STRATEGY.md required sections). Skill text stays the semantic
|
|
8
|
+
* SSOT (roadmap D5) — this module implements the deterministic subset and
|
|
9
|
+
* never forks semantics.
|
|
10
|
+
*
|
|
11
|
+
* Spec sources (each function cites the source section):
|
|
12
|
+
* - simplify:/temporary markers: `mstar-coding-behavior` SKILL.md § Simplicity
|
|
13
|
+
* First → "Simplification markers": a deliberate shortcut with a known
|
|
14
|
+
* ceiling is marked with a `simplify:` comment naming the ceiling and the
|
|
15
|
+
* upgrade path; a workaround is labeled `simplify:` / `temporary`, explains
|
|
16
|
+
* why, and records the removal path in the plan/status artifact before the
|
|
17
|
+
* task is claimed complete.
|
|
18
|
+
* - SDD TDD triple: `mstar-coding-behavior` SKILL.md § Integration Notes —
|
|
19
|
+
* completion evidence must include the TDD triple (test file(s), command,
|
|
20
|
+
* output) in `task-N-report.md`; `mstar-sdd/references/file-handoffs.md` —
|
|
21
|
+
* fix subagents append covering test file(s), command run, output.
|
|
22
|
+
* - Plan quality bar: `mstar-plan-artifacts/references/plan-quality-bar.md`
|
|
23
|
+
* § Quality checklist + `templates/plan.main.md` self-review
|
|
24
|
+
* ("Placeholder scan: no TBD").
|
|
25
|
+
* - Skill frontmatter contract: `mstar-skill-authoring` SKILL.md § Frontmatter
|
|
26
|
+
* Contract — `name` stable lowercase-hyphen; `description` is the trigger
|
|
27
|
+
* contract (not a workflow summary), third person.
|
|
28
|
+
* - STRATEGY.md structure: `mstar-strategy` SKILL.md § STRATEGY.md structure —
|
|
29
|
+
* six required sections.
|
|
30
|
+
*
|
|
31
|
+
* Enforcement depth: roadmap §8.5 C4 — v1 lints are non-blocking
|
|
32
|
+
* `ValidationResult`s; callers surface them as warnings.
|
|
33
|
+
*
|
|
34
|
+
* Severity mapping: structural gaps (missing name/description/sections,
|
|
35
|
+
* placeholder tokens, TDD-triple gaps, un-tracked temporary markers) are
|
|
36
|
+
* `medium`; style heuristics (pronouns, workflow-summary shape) are `low`.
|
|
37
|
+
*/
|
|
38
|
+
import type { GateResult } from "./core.js";
|
|
39
|
+
/**
|
|
40
|
+
* A `simplify:` marker found in a file, with its 1-based line number and the
|
|
41
|
+
* trimmed comment line (lint reporting).
|
|
42
|
+
*/
|
|
43
|
+
export type SimplifyMarker = {
|
|
44
|
+
line: number;
|
|
45
|
+
text: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Find `simplify:` marker comments (mstar-coding-behavior § Simplicity First
|
|
49
|
+
* → "Simplification markers": a deliberate shortcut with a known ceiling —
|
|
50
|
+
* global lock, O(n²) scan, naive heuristic — is marked with a `simplify:`
|
|
51
|
+
* comment naming the ceiling and the upgrade path).
|
|
52
|
+
*
|
|
53
|
+
* Heuristic (documented, conservative): a marker is a line where a comment
|
|
54
|
+
* introducer (`//`, `/*`, `*`, `#`, `;`, `--`) precedes `simplify:` —
|
|
55
|
+
* case-insensitive, any column (leading or trailing comments). Pure prose
|
|
56
|
+
* like "we simplify: the interface" carries no comment introducer and is
|
|
57
|
+
* never reported. No judgment about whether the marker actually names a
|
|
58
|
+
* ceiling/upgrade path — discovery only; callers may inspect `text`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function findSimplifyMarkers(fileText: string): SimplifyMarker[];
|
|
61
|
+
/**
|
|
62
|
+
* A `temporary` marker found in a file. `removalPath` is the recorded
|
|
63
|
+
* plan/status artifact reference (first pattern match, see
|
|
64
|
+
* `findTemporaryMarkers`), or `null` when the marker names no removal path.
|
|
65
|
+
*/
|
|
66
|
+
export type TemporaryMarker = {
|
|
67
|
+
line: number;
|
|
68
|
+
text: string;
|
|
69
|
+
removalPath: string | null;
|
|
70
|
+
};
|
|
71
|
+
/** Result of `findTemporaryMarkers`: the gate verdict plus the markers found
|
|
72
|
+
* (so callers can both lint and report). */
|
|
73
|
+
export type TemporaryMarkerResult = GateResult & {
|
|
74
|
+
markers: TemporaryMarker[];
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Find `temporary` label comments and check each carries a recorded removal
|
|
78
|
+
* path (plan/status artifact reference). Markers lacking a removal path are
|
|
79
|
+
* violations: `lint.temporary.no-removal-path` (mstar-coding-behavior §
|
|
80
|
+
* Simplicity First — "If a workaround is unavoidable, label it `simplify:` /
|
|
81
|
+
* `temporary`, explain why, and record the removal path in the plan/status
|
|
82
|
+
* artifact before claiming the task complete").
|
|
83
|
+
*
|
|
84
|
+
* Heuristic (documented, conservative): a marker is a comment-context line
|
|
85
|
+
* containing the word `temporary` (case-insensitive, word-boundary — so
|
|
86
|
+
* "temporarily" is not a label). Removal-path detection uses the pattern set
|
|
87
|
+
* above; a version mention alone ("remove in v2.0.0") is NOT a plan/status
|
|
88
|
+
* artifact reference and is reported, per the strict convention wording.
|
|
89
|
+
*
|
|
90
|
+
* Accepted false-positive (documented trade-off): the `;` / `--` comment
|
|
91
|
+
* introducers (Lisp/SQL comment syntax) can match code lines such as
|
|
92
|
+
* `foo(); temporary = 1;` — the `;` introducer + `\s*temporary\b` looks
|
|
93
|
+
* like a marker even though `temporary` is a variable name. Errs toward
|
|
94
|
+
* flagging (a violation prompts a removal path, which the code line won't
|
|
95
|
+
* carry); do not special-case it without a real FP report.
|
|
96
|
+
*/
|
|
97
|
+
export declare function findTemporaryMarkers(fileText: string): TemporaryMarkerResult;
|
|
98
|
+
/**
|
|
99
|
+
* Assert the SDD TDD triple is present in a `task-N-report.md` text
|
|
100
|
+
* (mstar-coding-behavior § Integration Notes — "completion evidence must
|
|
101
|
+
* include TDD triple — test file(s), command, output — in task-N-report.md";
|
|
102
|
+
* mstar-sdd/references/file-handoffs.md — fix subagents append covering test
|
|
103
|
+
* file(s), command run, output).
|
|
104
|
+
*
|
|
105
|
+
* One violation per missing part:
|
|
106
|
+
* - `lint.sdd-tdd.missing-tests` — no test file reference
|
|
107
|
+
* - `lint.sdd-tdd.missing-command` — no runnable command
|
|
108
|
+
* - `lint.sdd-tdd.missing-output` — no output evidence
|
|
109
|
+
*
|
|
110
|
+
* Heuristics (documented, conservative — tuned so prose alone never counts):
|
|
111
|
+
* - tests: a `.test.<ext>` / `.spec.<ext>` path, or the phrase "test file(s)"
|
|
112
|
+
* (the handoff template's exact header). "I added tests" without a file or
|
|
113
|
+
* the phrase does not count.
|
|
114
|
+
* - command: a `$`-prefixed line, or a known runner invocation (bun/pnpm/
|
|
115
|
+
* npm/yarn/npx/bunx test|run|exec, npx/bunx exec, tsc/vitest/jest/mocha/
|
|
116
|
+
* pytest/go test/cargo test). Prose "run the tests" names no runner and
|
|
117
|
+
* does not count (plan-quality-bar marks it a weak step anyway).
|
|
118
|
+
* - output: check marks, PASS/FAIL tokens, counts ("12 pass"), `N ok` /
|
|
119
|
+
* TAP `ok N` / "all ok" verdicts, exit-code statements. Bare prose
|
|
120
|
+
* `OK`/`ERROR` ("OK, moving on") does NOT count — output evidence must
|
|
121
|
+
* look like output. Line-based and fence-insensitive: real output usually
|
|
122
|
+
* lives in fenced blocks, so fence content is scanned too.
|
|
123
|
+
*/
|
|
124
|
+
export declare function assertSddTddTriple(reportText: string): GateResult;
|
|
125
|
+
/**
|
|
126
|
+
* One placeholder occurrence found by `planQualityBar`.
|
|
127
|
+
*/
|
|
128
|
+
export type PlanQualityFinding = {
|
|
129
|
+
/** Normalized token: `TBD`, `TODO`, `TBA`, or `...`. */
|
|
130
|
+
token: string;
|
|
131
|
+
/** 1-based line number. */
|
|
132
|
+
line: number;
|
|
133
|
+
/** Trimmed source line. */
|
|
134
|
+
text: string;
|
|
135
|
+
};
|
|
136
|
+
/** Result of `planQualityBar`: the gate verdict plus structured findings. */
|
|
137
|
+
export type PlanQualityResult = GateResult & {
|
|
138
|
+
findings: PlanQualityFinding[];
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Plan quality bar — placeholder scan (mstar-plan-artifacts
|
|
142
|
+
* `references/plan-quality-bar.md` § Quality checklist + `templates/
|
|
143
|
+
* plan.main.md` self-review "Placeholder scan: no TBD"). Every placeholder
|
|
144
|
+
* token found becomes one `lint.plan-quality.placeholder` violation whose
|
|
145
|
+
* message lists the token and location.
|
|
146
|
+
*
|
|
147
|
+
* Heuristic (documented, conservative):
|
|
148
|
+
* - tokens: `TBD`, `TODO`, `TBA` (case-insensitive, word-boundary, plural
|
|
149
|
+
* forms included) and the prose ellipsis `...`. One finding per line per
|
|
150
|
+
* token (a line with two TBDs yields one finding).
|
|
151
|
+
* - negation guard: a token preceded by a negation word (`no/not/without/
|
|
152
|
+
* none`) in the same segment (split at `(`/`[`/`{`/`.`/`;`/`,`/line
|
|
153
|
+
* start) is an absence assertion ("no TBD/placeholder/TODO" states the
|
|
154
|
+
* rule), not a placeholder — not flagged.
|
|
155
|
+
* - exemptions: fenced code blocks (```` ``` ```` / `~~~`) and inline code
|
|
156
|
+
* spans are skipped — `...` in a file-list or example is not a placeholder.
|
|
157
|
+
* - out of scope (judgment stays prompt): "add tests" without code, and
|
|
158
|
+
* `FIXME`/`XXX` code markers.
|
|
159
|
+
*/
|
|
160
|
+
export declare function planQualityBar(planText: string): PlanQualityResult;
|
|
161
|
+
/**
|
|
162
|
+
* Lint a skill file's frontmatter against the mstar-skill-authoring §
|
|
163
|
+
* Frontmatter Contract:
|
|
164
|
+
* - `name` — stable, lowercase-hyphen (`example-skill`);
|
|
165
|
+
* - `description` — the trigger contract, third person, not a workflow
|
|
166
|
+
* summary.
|
|
167
|
+
*
|
|
168
|
+
* Accepts a full document (leading `---`-fenced block is parsed) or a bare
|
|
169
|
+
* frontmatter body (`name:`/`description:` lines at the start). Violations:
|
|
170
|
+
* - `lint.frontmatter.missing` — no frontmatter block found
|
|
171
|
+
* - `lint.frontmatter.name.missing` — `name` absent
|
|
172
|
+
* - `lint.frontmatter.name.format` — `name` not lowercase-hyphen
|
|
173
|
+
* - `lint.frontmatter.description.missing` — `description` absent/empty
|
|
174
|
+
* - `lint.frontmatter.description.person` — first/second-person pronoun in
|
|
175
|
+
* the description (third-person heuristic, low severity)
|
|
176
|
+
* - `lint.frontmatter.description.workflow` — description reads as a
|
|
177
|
+
* workflow summary (verb-start or paragraph-length heuristic, low
|
|
178
|
+
* severity)
|
|
179
|
+
*
|
|
180
|
+
* Heuristics (documented, conservative; corpus regression tests in
|
|
181
|
+
* lint.test.ts cover the 20 real skill frontmatters):
|
|
182
|
+
* - pronouns: `I`/`we`/`you`/`my`/`our`/`your`/`us`, word-boundary,
|
|
183
|
+
* case-insensitive, after stripping quoted and backticked spans; `I/`
|
|
184
|
+
* (I/O) and all-caps `US` exempt.
|
|
185
|
+
* - workflow shape: description starts with a workflow verb ("Explains how
|
|
186
|
+
* …", "Describes …") — the contract's own bad example — or exceeds 120
|
|
187
|
+
* words (corpus max 114). Bold/quote prefixes are stripped before the
|
|
188
|
+
* verb check. No content judgment (e.g. whether the trigger is narrow
|
|
189
|
+
* enough) — that stays prompt.
|
|
190
|
+
*/
|
|
191
|
+
export declare function lintSkillFrontmatter(frontmatterText: string): GateResult;
|
|
192
|
+
/**
|
|
193
|
+
* Lint a STRATEGY.md document for the six required section headings
|
|
194
|
+
* (mstar-strategy § STRATEGY.md structure: Vision, What we build, What we
|
|
195
|
+
* don't build, Guiding Principles, Technology Direction, Decision Log).
|
|
196
|
+
* Optional sections (Current Focus, Risks & Mitigations, Competitive
|
|
197
|
+
* Context) are not required.
|
|
198
|
+
*
|
|
199
|
+
* Heuristic (documented, conservative): a section is a Markdown heading
|
|
200
|
+
* (`#`–`######`) whose text matches a required name exactly, case-
|
|
201
|
+
* insensitively, after stripping emphasis/backticks (so `## VISION`,
|
|
202
|
+
* `### What We Don't Build` all count). Partial matches ("Vision (short)")
|
|
203
|
+
* and other heading levels do not count. Each missing section is one
|
|
204
|
+
* `lint.strategy.missing-section` violation naming the heading.
|
|
205
|
+
*/
|
|
206
|
+
export declare function lintStrategySections(docText: string): GateResult;
|