@geraldmaron/construct 1.0.14 → 1.0.15
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 +6 -0
- package/bin/construct +3 -3
- package/commands/build/feature.md +0 -6
- package/commands/build/fix.md +0 -6
- package/commands/design/access.md +0 -6
- package/commands/design/flow.md +0 -6
- package/commands/design/ui.md +0 -6
- package/commands/measure/experiment.md +0 -6
- package/commands/measure/metrics.md +0 -6
- package/commands/measure/results.md +0 -6
- package/commands/plan/api.md +0 -6
- package/commands/plan/challenge.md +0 -6
- package/commands/plan/decide.md +0 -6
- package/commands/plan/feature.md +0 -7
- package/commands/plan/requirements.md +0 -6
- package/commands/remember/context.md +0 -6
- package/commands/remember/handoff.md +0 -6
- package/commands/remember/runbook.md +0 -6
- package/commands/review/code.md +0 -6
- package/commands/review/quality.md +0 -6
- package/commands/review/security.md +0 -6
- package/commands/ship/ready.md +0 -6
- package/commands/ship/release.md +0 -6
- package/commands/ship/status.md +0 -6
- package/commands/understand/docs.md +0 -6
- package/commands/understand/research.md +0 -6
- package/commands/understand/this.md +0 -6
- package/commands/understand/why.md +0 -6
- package/commands/work/clean.md +0 -6
- package/commands/work/drive.md +0 -6
- package/commands/work/optimize-prompts.md +0 -6
- package/commands/work/parallel-review.md +0 -6
- package/lib/beads-client.mjs +19 -1
- package/lib/comment-lint.mjs +5 -1
- package/lib/context-state.mjs +15 -3
- package/lib/contracts/validate.mjs +54 -10
- package/lib/contracts/violation-log.mjs +154 -0
- package/lib/document-extract/docling-client.mjs +114 -0
- package/lib/document-extract/docling-sidecar.py +122 -0
- package/lib/document-extract/whisper-client.mjs +79 -0
- package/lib/document-extract.mjs +73 -8
- package/lib/document-ingest.mjs +26 -4
- package/lib/embed/daemon.mjs +6 -1
- package/lib/flavors/loader.mjs +1 -1
- package/lib/handoffs/contract.mjs +2 -2
- package/lib/hooks/session-start.mjs +1 -1
- package/lib/ingest/chunker.mjs +94 -0
- package/lib/ingest/pipeline.mjs +53 -0
- package/lib/ingest/store.mjs +82 -0
- package/lib/knowledge/search.mjs +12 -0
- package/lib/mcp/server.mjs +142 -1
- package/lib/mcp/tools/skills.mjs +6 -3
- package/lib/mcp/tools/workflow.mjs +6 -2
- package/lib/observation-store.mjs +14 -5
- package/lib/ollama-manager.mjs +32 -28
- package/lib/prompt-composer.js +11 -1
- package/lib/runtime/uv-bootstrap.mjs +129 -0
- package/lib/runtime/whisper-bootstrap.mjs +102 -0
- package/lib/server/index.mjs +22 -1
- package/lib/server/static/index.html +1 -1
- package/lib/specialists/postconditions.mjs +1 -1
- package/lib/sync/skill-frontmatter.mjs +113 -21
- package/lib/tracking-surfaces.mjs +2 -0
- package/lib/validators/skills.mjs +86 -54
- package/package.json +5 -5
- package/personas/construct.md +4 -7
- package/platforms/claude/settings.template.json +1 -1
- package/rules/common/beads-hygiene.md +3 -9
- package/rules/common/code-review.md +3 -6
- package/rules/common/coding-style.md +3 -6
- package/rules/common/comments.md +3 -7
- package/rules/common/commit-approval.md +3 -6
- package/rules/common/cx-agent-routing.md +3 -7
- package/rules/common/cx-skill-routing.md +3 -3
- package/rules/common/doc-ownership.md +3 -8
- package/rules/common/efficiency.md +3 -8
- package/rules/common/framing.md +3 -8
- package/rules/common/git-workflow.md +3 -5
- package/rules/common/no-fabrication.md +4 -12
- package/rules/common/patterns.md +3 -5
- package/rules/common/release-gates.md +3 -8
- package/rules/common/research.md +3 -8
- package/rules/common/review-before-change.md +3 -9
- package/rules/common/security.md +3 -6
- package/rules/common/skill-composition.md +3 -7
- package/rules/common/testing.md +3 -6
- package/rules/golang/coding-style.md +1 -5
- package/rules/golang/hooks.md +1 -5
- package/rules/golang/patterns.md +1 -5
- package/rules/golang/security.md +1 -5
- package/rules/golang/testing.md +1 -5
- package/rules/python/coding-style.md +1 -5
- package/rules/python/hooks.md +1 -5
- package/rules/python/patterns.md +1 -5
- package/rules/python/security.md +1 -5
- package/rules/python/testing.md +1 -5
- package/rules/swift/coding-style.md +1 -5
- package/rules/swift/hooks.md +1 -5
- package/rules/swift/patterns.md +1 -5
- package/rules/swift/security.md +1 -5
- package/rules/swift/testing.md +1 -5
- package/rules/typescript/coding-style.md +1 -5
- package/rules/typescript/hooks.md +1 -5
- package/rules/typescript/patterns.md +1 -5
- package/rules/typescript/security.md +1 -5
- package/rules/typescript/testing.md +1 -5
- package/rules/web/coding-style.md +3 -5
- package/rules/web/design-quality.md +3 -5
- package/rules/web/hooks.md +3 -5
- package/rules/web/patterns.md +3 -5
- package/rules/web/performance.md +3 -5
- package/rules/web/security.md +3 -5
- package/rules/web/testing.md +3 -5
- package/skills/ai/agent-dev.md +4 -5
- package/skills/ai/llm-security.md +4 -5
- package/skills/ai/ml-ops.md +4 -5
- package/skills/ai/orchestration-workflow.md +4 -5
- package/skills/ai/prompt-and-eval.md +4 -5
- package/skills/ai/prompt-optimizer.md +4 -6
- package/skills/ai/rag-system.md +4 -5
- package/skills/architecture/api-design.md +4 -5
- package/skills/architecture/caching.md +4 -5
- package/skills/architecture/cloud-native.md +4 -5
- package/skills/architecture/message-queue.md +4 -5
- package/skills/architecture/security-arch.md +4 -5
- package/skills/compliance/ai-disclosure.md +4 -5
- package/skills/compliance/data-privacy.md +4 -5
- package/skills/compliance/license-audit.md +4 -5
- package/skills/compliance/regulatory-review.md +4 -5
- package/skills/development/cpp.md +4 -5
- package/skills/development/go.md +4 -5
- package/skills/development/java.md +4 -5
- package/skills/development/kotlin.md +4 -5
- package/skills/development/mobile-crossplatform.md +4 -5
- package/skills/development/python.md +4 -5
- package/skills/development/rust.md +4 -5
- package/skills/development/shell.md +4 -5
- package/skills/development/swift.md +4 -5
- package/skills/development/typescript.md +4 -5
- package/skills/devops/ci-cd.md +4 -5
- package/skills/devops/containerization.md +4 -5
- package/skills/devops/cost-optimization.md +4 -5
- package/skills/devops/data-engineering.md +4 -5
- package/skills/devops/database.md +4 -5
- package/skills/devops/dependency-management.md +4 -5
- package/skills/devops/devsecops.md +4 -5
- package/skills/devops/git-workflow.md +4 -5
- package/skills/devops/incident-response.md +4 -5
- package/skills/devops/monorepo.md +4 -5
- package/skills/devops/observability.md +4 -5
- package/skills/devops/performance.md +4 -5
- package/skills/devops/testing.md +4 -5
- package/skills/docs/adr-workflow.md +4 -7
- package/skills/docs/backlog-proposal-workflow.md +4 -3
- package/skills/docs/customer-profile-workflow.md +4 -3
- package/skills/docs/document-ingest-workflow.md +4 -3
- package/skills/docs/evidence-ingest-workflow.md +4 -3
- package/skills/docs/init-docs.md +4 -5
- package/skills/docs/init-project.md +4 -5
- package/skills/docs/prd-workflow.md +4 -7
- package/skills/docs/prfaq-workflow.md +4 -3
- package/skills/docs/product-intelligence-review.md +4 -3
- package/skills/docs/product-intelligence-workflow.md +4 -6
- package/skills/docs/product-signal-workflow.md +4 -5
- package/skills/docs/research-workflow.md +4 -5
- package/skills/docs/runbook-workflow.md +4 -5
- package/skills/docs/strategy-workflow.md +4 -5
- package/skills/exploration/dependency-graph-reading.md +4 -7
- package/skills/exploration/repo-map.md +4 -5
- package/skills/exploration/tracer-bullet-method.md +4 -7
- package/skills/exploration/unknown-codebase-onboarding.md +4 -7
- package/skills/frameworks/django.md +4 -5
- package/skills/frameworks/nextjs.md +4 -5
- package/skills/frameworks/react.md +4 -5
- package/skills/frameworks/spring-boot.md +4 -5
- package/skills/frontend-design/accessibility.md +4 -5
- package/skills/frontend-design/component-patterns.md +4 -5
- package/skills/frontend-design/engineering.md +4 -5
- package/skills/frontend-design/state-management.md +4 -5
- package/skills/frontend-design/ui-aesthetics.md +4 -5
- package/skills/frontend-design/ux-principles.md +4 -5
- package/skills/operating/change-management.md +4 -8
- package/skills/operating/incident-response.md +4 -8
- package/skills/operating/oncall-rotation.md +4 -7
- package/skills/operating/orchestration-reference.md +4 -10
- package/skills/quality-gates/review-work.md +4 -5
- package/skills/quality-gates/verify-change.md +4 -5
- package/skills/quality-gates/verify-module.md +4 -5
- package/skills/quality-gates/verify-quality.md +4 -5
- package/skills/quality-gates/verify-security.md +4 -5
- package/skills/roles/architect.ai-systems.md +6 -9
- package/skills/roles/architect.data.md +6 -9
- package/skills/roles/architect.enterprise.md +6 -9
- package/skills/roles/architect.integration.md +6 -9
- package/skills/roles/architect.md +7 -14
- package/skills/roles/architect.platform.md +6 -9
- package/skills/roles/data-analyst.experiment.md +6 -9
- package/skills/roles/data-analyst.md +6 -9
- package/skills/roles/data-analyst.product-intelligence.md +7 -9
- package/skills/roles/data-analyst.product.md +6 -9
- package/skills/roles/data-analyst.telemetry.md +7 -9
- package/skills/roles/data-engineer.pipeline.md +6 -9
- package/skills/roles/data-engineer.vector-retrieval.md +7 -9
- package/skills/roles/data-engineer.warehouse.md +6 -9
- package/skills/roles/debugger.md +6 -9
- package/skills/roles/designer.accessibility.md +6 -9
- package/skills/roles/designer.md +7 -9
- package/skills/roles/engineer.ai.md +6 -9
- package/skills/roles/engineer.data.md +6 -9
- package/skills/roles/engineer.md +9 -9
- package/skills/roles/engineer.platform.md +6 -9
- package/skills/roles/operator.docs.md +6 -9
- package/skills/roles/operator.md +9 -9
- package/skills/roles/operator.release.md +6 -9
- package/skills/roles/operator.sre.md +6 -9
- package/skills/roles/orchestrator.md +6 -9
- package/skills/roles/product-manager.ai-product.md +6 -9
- package/skills/roles/product-manager.business-strategy.md +6 -9
- package/skills/roles/product-manager.enterprise.md +6 -9
- package/skills/roles/product-manager.growth.md +7 -9
- package/skills/roles/product-manager.md +7 -9
- package/skills/roles/product-manager.platform.md +6 -9
- package/skills/roles/product-manager.product.md +6 -9
- package/skills/roles/qa.ai-eval.md +8 -9
- package/skills/roles/qa.api-contract.md +7 -9
- package/skills/roles/qa.data-pipeline.md +7 -9
- package/skills/roles/qa.md +7 -9
- package/skills/roles/qa.test-automation.md +6 -9
- package/skills/roles/qa.web-ui.md +7 -9
- package/skills/roles/researcher.explorer.md +6 -9
- package/skills/roles/researcher.md +8 -9
- package/skills/roles/researcher.ux.md +6 -9
- package/skills/roles/reviewer.devil-advocate.md +6 -9
- package/skills/roles/reviewer.evaluator.md +6 -9
- package/skills/roles/reviewer.md +9 -9
- package/skills/roles/reviewer.trace.md +6 -9
- package/skills/roles/security.ai.md +7 -9
- package/skills/roles/security.appsec.md +6 -9
- package/skills/roles/security.cloud.md +6 -9
- package/skills/roles/security.legal-compliance.md +6 -9
- package/skills/roles/security.md +7 -9
- package/skills/roles/security.privacy.md +7 -9
- package/skills/roles/security.supply-chain.md +7 -9
- package/skills/security/blue-team.md +4 -5
- package/skills/security/code-audit.md +4 -5
- package/skills/security/pentest.md +4 -5
- package/skills/security/red-team.md +4 -5
- package/skills/security/threat-intel.md +4 -5
- package/skills/security/vuln-research.md +4 -5
- package/skills/strategy/competitive-landscape.md +4 -8
- package/skills/strategy/market-research-methods.md +4 -8
- package/skills/strategy/narrative-arc.md +4 -8
- package/skills/strategy/pricing-positioning.md +4 -8
- package/skills/utility/clean-code.md +4 -5
- package/specialists/policy-inventory.json +14 -0
- package/lib/specialist-contracts-enforce.mjs +0 -172
- package/rules/common/agents.md +0 -28
- package/rules/common/development-workflow.md +0 -32
- package/rules/common/performance.md +0 -55
|
@@ -2,38 +2,130 @@
|
|
|
2
2
|
* lib/sync/skill-frontmatter.mjs — Build Anthropic Agent Skills frontmatter
|
|
3
3
|
* for SKILL.md files emitted by sync-specialists.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* extractSkillDescription pulls it out.
|
|
5
|
+
* Source skills carry YAML frontmatter with at minimum `name` + `description`
|
|
6
|
+
* (Anthropic spec). For role files, extra construct-internal keys (role,
|
|
7
|
+
* applies_to, inherits, version, profiles, cap) live in the same block; sync
|
|
8
|
+
* drops those at emit time so only spec-compliant keys ship in SKILL.md.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Backwards-compat path: source files missing YAML frontmatter fall through
|
|
11
|
+
* to HTML-comment preamble extraction, then first body paragraph. Keeps
|
|
12
|
+
* unmigrated files working during transition; the validator warns until
|
|
13
|
+
* they migrate.
|
|
14
|
+
*
|
|
15
|
+
* Zero-dep posture: runs from isolated environments (cpSync'd tmpdirs
|
|
16
|
+
* without node_modules), so the YAML reader/emitter stays in-tree. Surface
|
|
17
|
+
* is small — read flat top-level keys + simple inline arrays; write 2 keys
|
|
18
|
+
* (name, description). For richer YAML, the migration script and validator
|
|
19
|
+
* use js-yaml.
|
|
13
20
|
*/
|
|
14
21
|
|
|
22
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
15
23
|
const HTML_COMMENT_RE = /^<!--([\s\S]*?)-->/;
|
|
16
|
-
// Matches the source skill comment header shape: `skills/path/foo.md (Title)
|
|
17
|
-
// Use when X.\n...rest of comment...`. Group 1 captures the optional title,
|
|
18
|
-
// group 2 captures all subsequent prose inside the comment. No /m flag — `$`
|
|
19
|
-
// matches end of input, not end of line, so multi-line "Use when..." prose
|
|
20
|
-
// is captured in full and firstSentence() picks the first terminator.
|
|
21
|
-
|
|
22
24
|
const SOURCE_HEADER_RE = /^skills\/[\w./-]+\s*(?:\(([^)]+)\))?\s*([\s\S]*?)$/;
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const DESCRIPTION_MAX = 1024;
|
|
27
|
+
|
|
28
|
+
// In-tree minimal YAML parser. Handles the subset Construct skill frontmatter
|
|
29
|
+
// uses: flat scalar keys, double/single-quoted or unquoted strings, simple
|
|
30
|
+
// inline arrays ([a, b, c]), and block-style arrays (- item per line).
|
|
31
|
+
// Unparseable input returns null; callers fall back to extraction.
|
|
32
|
+
|
|
33
|
+
function parseFlatYaml(text) {
|
|
34
|
+
if (!text || typeof text !== 'string') return null;
|
|
35
|
+
const lines = text.split('\n');
|
|
36
|
+
const out = {};
|
|
37
|
+
let pendingArrayKey = null;
|
|
38
|
+
try {
|
|
39
|
+
for (const raw of lines) {
|
|
40
|
+
const line = raw.replace(/\r$/, '');
|
|
41
|
+
if (!line.trim() || line.trim().startsWith('#')) continue;
|
|
42
|
+
if (pendingArrayKey && /^\s+-\s+/.test(line)) {
|
|
43
|
+
out[pendingArrayKey].push(parseScalar(line.replace(/^\s+-\s+/, '')));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
pendingArrayKey = null;
|
|
47
|
+
const m = line.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/);
|
|
48
|
+
if (!m) continue;
|
|
49
|
+
const key = m[1];
|
|
50
|
+
const rawValue = m[2];
|
|
51
|
+
if (rawValue === '' || rawValue === undefined) {
|
|
52
|
+
out[key] = [];
|
|
53
|
+
pendingArrayKey = key;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
|
|
57
|
+
const inner = rawValue.slice(1, -1).trim();
|
|
58
|
+
out[key] = inner === '' ? [] : inner.split(',').map((s) => parseScalar(s.trim()));
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
out[key] = parseScalar(rawValue);
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err instanceof YamlError) return null;
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class YamlError extends Error {}
|
|
71
|
+
|
|
72
|
+
function parseScalar(s) {
|
|
73
|
+
const trimmed = s.trim();
|
|
74
|
+
if (trimmed === '' || trimmed === 'null' || trimmed === '~') return null;
|
|
75
|
+
if (trimmed === 'true') return true;
|
|
76
|
+
if (trimmed === 'false') return false;
|
|
77
|
+
if (/^-?\d+$/.test(trimmed)) return Number(trimmed);
|
|
78
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) return Number(trimmed);
|
|
79
|
+
if (trimmed.startsWith('"')) {
|
|
80
|
+
if (!trimmed.endsWith('"') || trimmed.length < 2) throw new YamlError('unclosed double quote');
|
|
81
|
+
return trimmed.slice(1, -1).replace(/\\"/g, '"');
|
|
82
|
+
}
|
|
83
|
+
if (trimmed.startsWith("'")) {
|
|
84
|
+
if (!trimmed.endsWith("'") || trimmed.length < 2) throw new YamlError('unclosed single quote');
|
|
85
|
+
return trimmed.slice(1, -1).replace(/\\'/g, "'");
|
|
86
|
+
}
|
|
87
|
+
return trimmed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function emitScalar(value) {
|
|
91
|
+
if (value === null || value === undefined) return 'null';
|
|
92
|
+
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
|
|
93
|
+
const s = String(value);
|
|
94
|
+
if (/[:"'\n#&*!|>%@`]|^[-?]/.test(s) || s.length === 0) {
|
|
95
|
+
return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
96
|
+
}
|
|
97
|
+
return s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function readSkillFrontmatter(content) {
|
|
101
|
+
if (!content) return null;
|
|
102
|
+
const match = content.match(FRONTMATTER_RE);
|
|
103
|
+
if (!match) return null;
|
|
104
|
+
return parseFlatYaml(match[1]);
|
|
105
|
+
}
|
|
27
106
|
|
|
28
107
|
export function buildSkillFrontmatter(name, sourceContent) {
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
108
|
+
const fromSource = readSkillFrontmatter(sourceContent);
|
|
109
|
+
const skillName = (fromSource?.name && typeof fromSource.name === 'string')
|
|
110
|
+
? fromSource.name
|
|
111
|
+
: kebabFromPath(name);
|
|
112
|
+
const description = (fromSource?.description && typeof fromSource.description === 'string')
|
|
113
|
+
? fromSource.description
|
|
114
|
+
: (extractSkillDescription(sourceContent) || `Construct skill: ${skillName}`);
|
|
115
|
+
const safe = description.replace(/\n+/g, ' ').slice(0, DESCRIPTION_MAX).replace(/"/g, "'").trim();
|
|
116
|
+
return `---\nname: ${emitScalar(skillName)}\ndescription: ${emitScalar(safe)}\n---\n`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function kebabFromPath(name) {
|
|
120
|
+
return String(name).replace(/[/.]/g, '-').toLowerCase();
|
|
33
121
|
}
|
|
34
122
|
|
|
35
123
|
export function extractSkillDescription(content) {
|
|
36
124
|
if (!content) return null;
|
|
125
|
+
const fromYaml = readSkillFrontmatter(content);
|
|
126
|
+
if (fromYaml?.description && typeof fromYaml.description === 'string') {
|
|
127
|
+
return fromYaml.description;
|
|
128
|
+
}
|
|
37
129
|
const commentMatch = content.match(HTML_COMMENT_RE);
|
|
38
130
|
if (commentMatch) {
|
|
39
131
|
const inner = commentMatch[1].trim();
|
|
@@ -45,7 +137,7 @@ export function extractSkillDescription(content) {
|
|
|
45
137
|
const lines = inner.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
46
138
|
if (lines.length >= 2) return firstSentence(lines.slice(1).join(' '));
|
|
47
139
|
}
|
|
48
|
-
const body = content.replace(HTML_COMMENT_RE, '').trim();
|
|
140
|
+
const body = content.replace(FRONTMATTER_RE, '').replace(HTML_COMMENT_RE, '').trim();
|
|
49
141
|
const paragraphs = body.split(/\n\s*\n/);
|
|
50
142
|
for (const p of paragraphs) {
|
|
51
143
|
const trimmed = p.trim();
|
|
@@ -26,6 +26,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSy
|
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
|
|
28
28
|
import { extractBeadsFromPlan, syncPlanWithBeads } from './beads-automation.mjs';
|
|
29
|
+
import { assertBeadId } from './beads-client.mjs';
|
|
29
30
|
import { buildPlanTemplate } from './project-init-shared.mjs';
|
|
30
31
|
|
|
31
32
|
const CONTEXT_RECENT_DAYS = 30;
|
|
@@ -191,6 +192,7 @@ export async function closeBeadsFromPrRefs({ prNumber, mergeCommitSha = '', cwd
|
|
|
191
192
|
: `Merged via PR #${prNumber}`;
|
|
192
193
|
|
|
193
194
|
for (const id of beadIds) {
|
|
195
|
+
try { assertBeadId(id); } catch (err) { errors.push({ id, reason: 'invalid-bead-id', detail: err.message }); continue; }
|
|
194
196
|
const status = readBeadStatus(cwd, id);
|
|
195
197
|
if (status === 'closed') { skipped.push(id); continue; }
|
|
196
198
|
if (status === null) { errors.push({ id, reason: 'bd-show-failed' }); continue; }
|
|
@@ -1,62 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/validators/skills.mjs — Validate the structure of skill files.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* skill paths) that follow this convention:
|
|
4
|
+
* After the YAML-frontmatter migration, every skill file under `skills/` has:
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* ---
|
|
7
|
+
* name: <kebab-case>
|
|
8
|
+
* description: "<≤1024 chars, includes a 'use when' trigger>"
|
|
9
|
+
* [role/applies_to/inherits/version/profiles/cap for role files]
|
|
10
|
+
* ---
|
|
11
11
|
* # <Title>
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* ...
|
|
12
|
+
* Use when: <trigger condition>
|
|
13
|
+
* <body>
|
|
15
14
|
*
|
|
16
15
|
* The validator splits findings into hard errors (block the build) and soft
|
|
17
16
|
* warnings (surfaced by `construct doctor` without failing it):
|
|
18
17
|
*
|
|
19
18
|
* Hard errors:
|
|
20
|
-
* - missing
|
|
19
|
+
* - frontmatter missing or not parseable as YAML
|
|
20
|
+
* - name missing, > 64 chars, fails ^[a-z0-9][a-z0-9-]*$, or contains
|
|
21
|
+
* reserved tokens (anthropic, claude)
|
|
22
|
+
* - description missing, empty, > 1024 chars, contains XML/HTML tags,
|
|
23
|
+
* or missing a "use when" trigger clause
|
|
24
|
+
* - missing H1 title (in body)
|
|
21
25
|
* - title over 80 chars
|
|
22
26
|
* - duplicate relative paths across roots
|
|
23
27
|
* - unreadable file
|
|
24
28
|
*
|
|
25
29
|
* Soft warnings:
|
|
26
|
-
* - opener
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* - opener over 240 chars
|
|
30
|
-
*
|
|
31
|
-
* Hard checks prevent broken file structure; soft checks surface drift from
|
|
32
|
-
* authoring conventions without blocking authors who legitimately diverge.
|
|
30
|
+
* - body opener missing or doesn't match a "Use this skill when/to/for ..."
|
|
31
|
+
* form (YAML description is now primary; body opener is a behavioral hint)
|
|
32
|
+
* - body opener over 240 chars
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
36
36
|
import path from 'node:path';
|
|
37
|
+
import yaml from 'js-yaml';
|
|
37
38
|
|
|
38
|
-
const
|
|
39
|
+
const DESCRIPTION_MAX = 1024;
|
|
40
|
+
const NAME_MAX = 64;
|
|
39
41
|
const TITLE_MAX_CHARS = 80;
|
|
42
|
+
const BODY_OPENER_MAX = 240;
|
|
43
|
+
const NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
44
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
40
45
|
|
|
41
|
-
// Accept any of the conventional skill-trigger openers actually in use across
|
|
42
|
-
// the corpus: "Use this skill when/to/for", "Use when:", "Use this when",
|
|
43
|
-
// "Trigger:", or a leading "When ..." that signals a triggering condition.
|
|
44
46
|
const TRIGGER_PATTERN = /^(?:use\s+(?:this\s+(?:skill\s+)?)?(?:when|to|for|if)\b|when\s+to\s+use\b|trigger\s*:|use\s+when\s*:)/i;
|
|
45
47
|
|
|
46
48
|
function* walk(root) {
|
|
47
|
-
const
|
|
48
|
-
for (const entry of entries) {
|
|
49
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
49
50
|
const full = path.join(root, entry.name);
|
|
50
|
-
if (entry.isDirectory())
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
if (entry.isDirectory()) yield* walk(full);
|
|
52
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) yield full;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function parseFrontmatter(content) {
|
|
57
|
+
const match = content.match(FRONTMATTER_RE);
|
|
58
|
+
if (!match) return { frontmatter: null, body: content };
|
|
59
|
+
try {
|
|
60
|
+
const parsed = yaml.load(match[1]) || {};
|
|
61
|
+
return { frontmatter: parsed, body: content.slice(match[0].length) };
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return { frontmatter: null, body: content.slice(match[0].length), yamlError: err.message };
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
function extractTitleAndOpener(
|
|
59
|
-
const lines =
|
|
67
|
+
function extractTitleAndOpener(body) {
|
|
68
|
+
const lines = body.split(/\r?\n/);
|
|
60
69
|
let title = null;
|
|
61
70
|
let opener = null;
|
|
62
71
|
let inBody = false;
|
|
@@ -64,11 +73,7 @@ function extractTitleAndOpener(text) {
|
|
|
64
73
|
const line = lines[i].trim();
|
|
65
74
|
if (!title) {
|
|
66
75
|
const m = line.match(/^#\s+(.+?)\s*$/);
|
|
67
|
-
if (m) {
|
|
68
|
-
title = m[1];
|
|
69
|
-
inBody = true;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
76
|
+
if (m) { title = m[1]; inBody = true; continue; }
|
|
72
77
|
} else if (inBody && line && !line.startsWith('#') && !line.startsWith('<!--')) {
|
|
73
78
|
opener = line;
|
|
74
79
|
break;
|
|
@@ -77,23 +82,31 @@ function extractTitleAndOpener(text) {
|
|
|
77
82
|
return { title, opener };
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
function validateName(name) {
|
|
86
|
+
if (!name) return 'frontmatter.name is missing';
|
|
87
|
+
if (typeof name !== 'string') return `frontmatter.name must be a string (got ${typeof name})`;
|
|
88
|
+
if (!NAME_RE.test(name)) return `frontmatter.name "${name}" must match ${NAME_RE}`;
|
|
89
|
+
if (name.length > NAME_MAX) return `frontmatter.name "${name}" exceeds ${NAME_MAX} chars`;
|
|
90
|
+
if (/\banthropic\b|\bclaude\b/.test(name)) return `frontmatter.name "${name}" contains reserved token`;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateDescription(description) {
|
|
95
|
+
if (!description) return 'frontmatter.description is missing or empty';
|
|
96
|
+
if (typeof description !== 'string') return `frontmatter.description must be a string (got ${typeof description})`;
|
|
97
|
+
if (description.length > DESCRIPTION_MAX) return `frontmatter.description ${description.length} > ${DESCRIPTION_MAX} chars`;
|
|
98
|
+
if (/<[A-Za-z][^>]*>/.test(description)) return 'frontmatter.description contains XML/HTML tags';
|
|
99
|
+
if (!/use\s+when|use\s+this/i.test(description)) return 'frontmatter.description missing "use when" trigger clause';
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
export function validateSkills(roots) {
|
|
92
104
|
const dirs = Array.isArray(roots) ? roots : [roots];
|
|
93
105
|
const errors = [];
|
|
94
106
|
const warnings = [];
|
|
95
107
|
const skills = [];
|
|
96
108
|
const seenRelative = new Map();
|
|
109
|
+
const seenNames = new Map();
|
|
97
110
|
|
|
98
111
|
for (const dir of dirs) {
|
|
99
112
|
let exists = false;
|
|
@@ -113,12 +126,31 @@ export function validateSkills(roots) {
|
|
|
113
126
|
|
|
114
127
|
let raw;
|
|
115
128
|
try { raw = readFileSync(filePath, 'utf8'); }
|
|
116
|
-
catch (err) {
|
|
117
|
-
|
|
129
|
+
catch (err) { errors.push(`${rel}: cannot read (${err.message})`); continue; }
|
|
130
|
+
|
|
131
|
+
const { frontmatter, body, yamlError } = parseFrontmatter(raw);
|
|
132
|
+
|
|
133
|
+
if (yamlError) {
|
|
134
|
+
errors.push(`${rel}: frontmatter YAML parse error — ${yamlError}`);
|
|
118
135
|
continue;
|
|
119
136
|
}
|
|
137
|
+
if (!frontmatter) {
|
|
138
|
+
errors.push(`${rel}: missing YAML frontmatter block (---name/description---)`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const nameErr = validateName(frontmatter.name);
|
|
143
|
+
if (nameErr) errors.push(`${rel}: ${nameErr}`);
|
|
144
|
+
else {
|
|
145
|
+
const dupName = seenNames.get(frontmatter.name);
|
|
146
|
+
if (dupName) errors.push(`${rel}: duplicate frontmatter.name "${frontmatter.name}" (also in ${dupName})`);
|
|
147
|
+
else seenNames.set(frontmatter.name, rel);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const descErr = validateDescription(frontmatter.description);
|
|
151
|
+
if (descErr) errors.push(`${rel}: ${descErr}`);
|
|
120
152
|
|
|
121
|
-
const { title, opener } = extractTitleAndOpener(
|
|
153
|
+
const { title, opener } = extractTitleAndOpener(body);
|
|
122
154
|
|
|
123
155
|
if (!title) {
|
|
124
156
|
errors.push(`${rel}: missing H1 title (e.g. "# Skill Name")`);
|
|
@@ -127,14 +159,14 @@ export function validateSkills(roots) {
|
|
|
127
159
|
}
|
|
128
160
|
|
|
129
161
|
if (!opener) {
|
|
130
|
-
warnings.push(`${rel}: no trigger opener after the H1
|
|
162
|
+
warnings.push(`${rel}: no trigger opener in body after the H1 — YAML description still primary, but body opener helps in-task behavior`);
|
|
131
163
|
} else if (!TRIGGER_PATTERN.test(opener)) {
|
|
132
|
-
warnings.push(`${rel}: opener should start with "Use this skill when/to/for ..." (got "${opener.slice(0, 60)}…")`);
|
|
133
|
-
} else if (opener.length >
|
|
134
|
-
warnings.push(`${rel}: opener exceeds ${
|
|
164
|
+
warnings.push(`${rel}: body opener should start with "Use this skill when/to/for ..." (got "${opener.slice(0, 60)}…")`);
|
|
165
|
+
} else if (opener.length > BODY_OPENER_MAX) {
|
|
166
|
+
warnings.push(`${rel}: body opener exceeds ${BODY_OPENER_MAX} chars (got ${opener.length})`);
|
|
135
167
|
}
|
|
136
168
|
|
|
137
|
-
skills.push({ path: rel, title, opener });
|
|
169
|
+
skills.push({ path: rel, name: frontmatter.name, description: frontmatter.description, title, opener });
|
|
138
170
|
}
|
|
139
171
|
}
|
|
140
172
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geraldmaron/construct",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces",
|
|
6
6
|
"bin": {
|
|
@@ -57,7 +57,6 @@
|
|
|
57
57
|
"lint:contracts": "node ./bin/construct lint:contracts",
|
|
58
58
|
"lint:agents": "node ./bin/construct lint:agents",
|
|
59
59
|
"build:sea": "esbuild bin/construct --bundle --platform=node --target=node20 --packages=external --outfile=dist/construct-bundle.cjs 2>&1 || (echo 'esbuild not found — install with: npm install -g esbuild' && exit 1)",
|
|
60
|
-
"lint:prose": "node scripts/lint-prose.mjs",
|
|
61
60
|
"lint:profiles": "node scripts/lint-profiles.mjs",
|
|
62
61
|
"learning:status": "node scripts/learning-status.mjs",
|
|
63
62
|
"lint:templates": "node scripts/lint-commits-pr.mjs",
|
|
@@ -73,16 +72,17 @@
|
|
|
73
72
|
"@huggingface/transformers": "^4.2.0",
|
|
74
73
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
75
74
|
"@xenova/transformers": "^2.0.1",
|
|
75
|
+
"js-yaml": "^4.2.0",
|
|
76
76
|
"node-webvtt": "^1.9.3",
|
|
77
77
|
"postgres": "^3.4.9"
|
|
78
78
|
},
|
|
79
79
|
"optionalDependencies": {
|
|
80
80
|
"@opentelemetry/api": "^1.9.0",
|
|
81
|
-
"@opentelemetry/
|
|
81
|
+
"@opentelemetry/core": "^1.25.0",
|
|
82
82
|
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
|
83
83
|
"@opentelemetry/resources": "^1.25.0",
|
|
84
|
-
"@opentelemetry/
|
|
85
|
-
"@opentelemetry/
|
|
84
|
+
"@opentelemetry/sdk-trace-node": "^1.25.0",
|
|
85
|
+
"@opentelemetry/semantic-conventions": "^1.25.0"
|
|
86
86
|
},
|
|
87
87
|
"overrides": {
|
|
88
88
|
"express-rate-limit": "8.5.1",
|
package/personas/construct.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
routing rules, approval boundaries, and output contract. Loaded by sync-specialists
|
|
6
|
-
and emitted to every supported platform.
|
|
7
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
name: construct
|
|
3
|
+
description: Construct persona prompt.
|
|
4
|
+
---
|
|
8
5
|
You are Construct. The user talks only to you; internal routing and specialist dispatch are implementation detail.
|
|
9
6
|
|
|
10
7
|
**Anti-fabrication contract**: every load-bearing claim cites a verifiable source. Missing source becomes `unknown` or `[unverified]`. Specialists tailor; the persona never weakens. See `rules/common/no-fabrication.md`.
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
}
|
|
152
152
|
],
|
|
153
153
|
"id": "post:edit:comment-lint",
|
|
154
|
-
"description": "Block edits that violate comment policy (banned patterns, missing required header).
|
|
154
|
+
"description": "Block edits that violate comment policy (banned patterns, missing required header). No bypass — repair the policy if it fires wrong."
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
"matcher": "Write|Edit|MultiEdit",
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Beads is the system of record for durable work in this project. Status drifts
|
|
5
|
-
when issues are not updated alongside the code, so every agent and persona
|
|
6
|
-
operating in Construct treats hygiene as part of the work, not as cleanup.
|
|
7
|
-
Loaded by AGENTS.md, CLAUDE.md, the construct persona, and the engineer /
|
|
8
|
-
operator / planner role overlays.
|
|
9
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: Beads issue tracker hygiene contract.
|
|
3
|
+
---
|
|
10
4
|
# Beads Hygiene: Project Contract
|
|
11
5
|
|
|
12
6
|
Beads (`bd`) is the canonical durable tracker for Construct. Beads only earn their keep when their state matches the world. Stale "open" issues pollute `bd ready`, hide real work, and let agents propose work that already shipped. Every agent (human or AI, on any platform) is responsible for keeping the tracker honest.
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Defines mandatory review triggers, severity levels, approval criteria,
|
|
5
|
-
and references coding-style.md and security.md for checklists.
|
|
6
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: when and how to conduct code reviews.
|
|
3
|
+
---
|
|
7
4
|
# Code Review Standards
|
|
8
5
|
|
|
9
6
|
## When to Review
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Covers immutability, core principles (KISS/DRY/YAGNI), file organization,
|
|
5
|
-
error handling, input validation, naming conventions, and quality checklist.
|
|
6
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: language-agnostic coding standards.
|
|
3
|
+
---
|
|
7
4
|
# Coding Style
|
|
8
5
|
|
|
9
6
|
## Immutability (CRITICAL)
|
package/rules/common/comments.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Defines the two allowed comment forms (file header, section context block) and
|
|
5
|
-
what is never allowed (inline narration, trailing comments, mid-function notes).
|
|
6
|
-
Enforced by lib/hooks/comment-lint.mjs and tests/hooks-budget.test.mjs.
|
|
7
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: Construct comment convention for JS/TS/MJS source files.
|
|
3
|
+
---
|
|
8
4
|
# Comment Convention
|
|
9
5
|
|
|
10
6
|
Two forms are allowed. Everything else is deleted.
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Behavioral rule, not a hook. The agent asks and waits for a yes; the user
|
|
5
|
-
replies in chat. Infrastructure stays out of the way.
|
|
6
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: conversational approval rule for mutating git operations.
|
|
3
|
+
---
|
|
7
4
|
# Commit Approval
|
|
8
5
|
|
|
9
6
|
Construct does not commit, push, or merge without the user explicitly saying yes in the current conversation.
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Defines when to route directly to a cx-* specialist vs through Construct orchestration.
|
|
5
|
-
Covers intent-based routing table, complexity gate, and routing rules.
|
|
6
|
-
Loaded by rule-loading systems that look for cx-agent-routing in the rules hierarchy.
|
|
7
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: auto-trigger routing rules for cx-* specialist agents.
|
|
3
|
+
---
|
|
8
4
|
# cx-* Agent Routing: Auto-trigger Rules
|
|
9
5
|
|
|
10
6
|
When a request matches the trigger patterns below, automatically route to the corresponding cx-* specialist or persona before responding.
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Prevents the orchestrator (or any general persona) from authoring specialist
|
|
5
|
-
documents directly. Routing authorship to the owning role is how research,
|
|
6
|
-
framing, and domain scrutiny actually fire: writing a PRD without the
|
|
7
|
-
product manager bypasses those checks entirely.
|
|
8
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: which specialist owns which document type.
|
|
3
|
+
---
|
|
9
4
|
# Document Ownership
|
|
10
5
|
|
|
11
6
|
A document type names a body of work. That body of work has an owner. The orchestrator routes; it does not author.
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Applies to all agents operating in a Construct session. Violations compound context
|
|
5
|
-
cost and reduce throughput. These rules are enforced by read-tracker.mjs and surfaced
|
|
6
|
-
in the session-start efficiency digest.
|
|
7
|
-
-->
|
|
8
|
-
|
|
1
|
+
---
|
|
2
|
+
description: session context and tool-use efficiency standards.
|
|
3
|
+
---
|
|
9
4
|
# Session Efficiency
|
|
10
5
|
|
|
11
6
|
## Read discipline
|
package/rules/common/framing.md
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Establishes the hard separation between execution artifacts (tickets, chat
|
|
5
|
-
transcripts, existing docs) and sources of truth (the underlying problem).
|
|
6
|
-
Applies to every specialist working on architecture, documentation, research,
|
|
7
|
-
product, or strategy work. Read before scaffolding anything.
|
|
8
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: how to frame a problem before acting on it.
|
|
3
|
+
---
|
|
9
4
|
# Framing Policy
|
|
10
5
|
|
|
11
6
|
Most agent failures on ambiguous work trace to a single mistake: anchoring on the most concrete input available (usually a ticket or a prior doc) and building the output around its structure instead of around the actual problem.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Defines conventional commit types and pull request creation steps.
|
|
5
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: commit message format and PR workflow.
|
|
3
|
+
---
|
|
6
4
|
# Git Workflow
|
|
7
5
|
|
|
8
6
|
## Commit Message Format
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Defines the trust contract between operators and the system: outputs stick to
|
|
5
|
-
source, gaps stay visible, and confidence reflects evidence. Applies to every
|
|
6
|
-
specialist, every artifact, and every summary — intake processing, document
|
|
7
|
-
evaluation, knowledge writing, plan drafting, review verdicts, handoffs.
|
|
8
|
-
|
|
9
|
-
Sibling rules: research.md (evidence hierarchy), framing.md (execution
|
|
10
|
-
artifacts are not sources), comments.md (banned voice patterns).
|
|
11
|
-
-->
|
|
1
|
+
---
|
|
2
|
+
description: canonical anti-fabrication policy for Construct.
|
|
3
|
+
---
|
|
12
4
|
# No-Fabrication Policy
|
|
13
5
|
|
|
14
6
|
Fabrication is the single largest threat to trust in an agent system. A persona that invents a customer quote, sharpens a vague signal into a confident assertion, or papers over a gap with plausible-sounding prose corrupts every artifact downstream. This rule applies to **every output** Construct produces: intake summaries, classification rationales, PRDs, ADRs, RFCs, knowledge notes, handoffs, review verdicts, plan entries, beads issues, MCP tool responses, dashboard text.
|
|
@@ -61,7 +53,7 @@ Fabrication is the single largest threat to trust in an agent system. A persona
|
|
|
61
53
|
## Enforcement
|
|
62
54
|
|
|
63
55
|
- `lib/comment-lint.mjs` enforces a subset of these patterns on artifact paths (`docs/prd/**`, `docs/adr/**`, `docs/rfc/**`, `docs/research/**`, `.cx/knowledge/**`, `.cx/handoffs/**`, `.cx/research/**`). PostToolUse warns; `npm run lint:comments`, `construct lint:comments`, and the release gate block.
|
|
64
|
-
- `specialists/contracts.json` postconditions check structural requirements (mandatory sections, intake traceability, citation density). `lib/contracts/validate.mjs#validateHandoff` blocks handoffs that fail validation
|
|
56
|
+
- `specialists/contracts.json` postconditions check structural requirements (mandatory sections, intake traceability, citation density). `lib/contracts/validate.mjs#validateHandoff` blocks handoffs that fail validation; binary postconditions in `lib/specialists/postconditions.mjs` block rubber-stamp reviews, post-hoc threat models, symptom-only fixes, stale-doc PRs, and post-hoc accessibility. Enforcement is hard-default `block`.
|
|
65
57
|
- `construct intake done <id> --output=<path>` stamps `intake_id`, `intake_confidence`, and `intake_rationale` into the artifact's frontmatter so every intake-derived artifact carries verifiable provenance.
|
|
66
58
|
|
|
67
59
|
## Bypass
|