universal-dev-standards 6.8.0 → 6.10.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/bin/uds.js +12 -2
- package/bundled/ai/standards/acceptance-criteria-traceability.ai.yaml +14 -2
- package/bundled/ai/standards/adr-standards.ai.yaml +14 -2
- package/bundled/ai/standards/code-review.ai.yaml +13 -3
- package/bundled/ai/standards/commit-message.ai.yaml +8 -4
- package/bundled/ai/standards/deferred-item-exit.ai.yaml +225 -0
- package/bundled/ai/standards/feature-discovery-standards.ai.yaml +14 -2
- package/bundled/ai/standards/governance-layer.ai.yaml +128 -2
- package/bundled/ai/standards/logging.ai.yaml +2 -2
- package/bundled/ai/standards/retrospective-standards.ai.yaml +14 -2
- package/bundled/ai/standards/reverse-engineering-standards.ai.yaml +73 -2
- package/bundled/ai/standards/security-standards.ai.yaml +2 -2
- package/bundled/ai/standards/spec-driven-development.ai.yaml +14 -2
- package/bundled/ai/standards/tech-debt-standards.ai.yaml +87 -3
- package/bundled/ai/standards/turn-completion-integrity.ai.yaml +131 -0
- package/bundled/core/acceptance-criteria-traceability.md +5 -2
- package/bundled/core/adr-standards.md +26 -2
- package/bundled/core/agent-communication-protocol.md +8 -0
- package/bundled/core/branch-completion.md +8 -0
- package/bundled/core/change-batching-standards.md +8 -0
- package/bundled/core/code-review-checklist.md +5 -2
- package/bundled/core/context-aware-loading.md +1 -1
- package/bundled/core/deferred-item-exit.md +254 -0
- package/bundled/core/execution-history.md +8 -0
- package/bundled/core/feature-discovery-standards.md +5 -1
- package/bundled/core/governance-layer.md +114 -2
- package/bundled/core/pipeline-integration-standards.md +8 -0
- package/bundled/core/retrospective-standards.md +4 -2
- package/bundled/core/reverse-engineering-standards.md +81 -2
- package/bundled/core/spec-driven-development.md +8 -2
- package/bundled/core/tech-debt-standards.md +67 -8
- package/bundled/core/turn-completion-integrity.md +196 -0
- package/bundled/core/workflow-enforcement.md +8 -0
- package/bundled/core/workflow-state-protocol.md +8 -0
- package/bundled/hooks/check-dangerous-cmd.mjs +60 -0
- package/bundled/hooks/check-logging-standard.mjs +59 -0
- package/bundled/hooks/check-turn-completion.mjs +233 -0
- package/bundled/hooks/inject-standards.mjs +183 -0
- package/bundled/hooks/telemetry-wrapper.mjs +77 -0
- package/bundled/hooks/turn-completion/detect.mjs +99 -0
- package/bundled/hooks/turn-completion/locales/en.mjs +159 -0
- package/bundled/hooks/turn-completion/locales/zh-TW.mjs +166 -0
- package/bundled/hooks/validate-commit-msg.mjs +104 -0
- package/bundled/locales/zh-CN/CHANGELOG.md +73 -3
- package/bundled/locales/zh-CN/CLAUDE.md +1 -1
- package/bundled/locales/zh-CN/README.md +2 -2
- package/bundled/locales/zh-CN/SECURITY.md +1 -1
- package/bundled/locales/zh-CN/core/adr-standards.md +1 -1
- package/bundled/locales/zh-CN/core/agent-communication-protocol.md +7 -0
- package/bundled/locales/zh-CN/core/branch-completion.md +7 -0
- package/bundled/locales/zh-CN/core/change-batching-standards.md +7 -0
- package/bundled/locales/zh-CN/core/execution-history.md +7 -0
- package/bundled/locales/zh-CN/core/governance-layer.md +118 -6
- package/bundled/locales/zh-CN/core/pipeline-integration-standards.md +7 -0
- package/bundled/locales/zh-CN/core/retrospective-standards.md +1 -1
- package/bundled/locales/zh-CN/core/tech-debt-standards.md +71 -4
- package/bundled/locales/zh-CN/core/turn-completion-integrity.md +190 -0
- package/bundled/locales/zh-CN/core/workflow-enforcement.md +7 -0
- package/bundled/locales/zh-CN/core/workflow-state-protocol.md +7 -0
- package/bundled/locales/zh-CN/docs/CHEATSHEET.md +8 -1
- package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +29 -68
- package/bundled/locales/zh-CN/docs/FEATURE-REFERENCE.md +25 -15
- package/bundled/locales/zh-CN/docs/MIGRATION-v6.md +8 -4
- package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +1 -2
- package/bundled/locales/zh-CN/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
- package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +3 -3
- package/bundled/locales/zh-CN/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +22 -12
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +12 -9
- package/bundled/locales/zh-CN/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/commands/brainstorm.md +17 -13
- package/bundled/locales/zh-CN/skills/commands/config.md +0 -1
- package/bundled/locales/zh-CN/skills/commands/init.md +1 -2
- package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/release-standards/SKILL.md +3 -0
- package/bundled/locales/zh-CN/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/locales/zh-CN/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-CN/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/CHANGELOG.md +74 -3
- package/bundled/locales/zh-TW/CLAUDE.md +1 -1
- package/bundled/locales/zh-TW/README.md +2 -2
- package/bundled/locales/zh-TW/SECURITY.md +1 -1
- package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +2 -0
- package/bundled/locales/zh-TW/core/adr-standards.md +26 -5
- package/bundled/locales/zh-TW/core/agent-communication-protocol.md +7 -0
- package/bundled/locales/zh-TW/core/branch-completion.md +7 -0
- package/bundled/locales/zh-TW/core/change-batching-standards.md +7 -0
- package/bundled/locales/zh-TW/core/code-review-checklist.md +2 -0
- package/bundled/locales/zh-TW/core/container-image-standards.md +2 -2
- package/bundled/locales/zh-TW/core/contract-testing-standards.md +2 -2
- package/bundled/locales/zh-TW/core/cross-flow-regression.md +8 -7
- package/bundled/locales/zh-TW/core/data-contract.md +2 -2
- package/bundled/locales/zh-TW/core/data-migration-testing.md +2 -2
- package/bundled/locales/zh-TW/core/data-pipeline.md +2 -2
- package/bundled/locales/zh-TW/core/deferred-item-exit.md +251 -0
- package/bundled/locales/zh-TW/core/documentation-writing-standards.md +228 -3
- package/bundled/locales/zh-TW/core/execution-history.md +7 -0
- package/bundled/locales/zh-TW/core/full-coverage-testing.md +15 -2
- package/bundled/locales/zh-TW/core/governance-layer.md +118 -5
- package/bundled/locales/zh-TW/core/iac-design-principles.md +2 -2
- package/bundled/locales/zh-TW/core/incident-response.md +2 -2
- package/bundled/locales/zh-TW/core/model-provenance.md +4 -2
- package/bundled/locales/zh-TW/core/pii-classification.md +42 -6
- package/bundled/locales/zh-TW/core/pipeline-integration-standards.md +7 -0
- package/bundled/locales/zh-TW/core/prd-standards.md +4 -2
- package/bundled/locales/zh-TW/core/product-metrics-standards.md +4 -2
- package/bundled/locales/zh-TW/core/release-readiness-gate.md +2 -2
- package/bundled/locales/zh-TW/core/resource-cost-boundary.md +2 -2
- package/bundled/locales/zh-TW/core/retrospective-standards.md +5 -3
- package/bundled/locales/zh-TW/core/reverse-engineering-standards.md +83 -5
- package/bundled/locales/zh-TW/core/runbook.md +2 -2
- package/bundled/locales/zh-TW/core/schema-evolution.md +2 -2
- package/bundled/locales/zh-TW/core/secret-management-standards.md +2 -2
- package/bundled/locales/zh-TW/core/slo-sli.md +2 -2
- package/bundled/locales/zh-TW/core/spec-driven-development.md +2 -0
- package/bundled/locales/zh-TW/core/tech-debt-standards.md +71 -4
- package/bundled/locales/zh-TW/core/turn-completion-integrity.md +190 -0
- package/bundled/locales/zh-TW/core/user-journey-testing.md +2 -2
- package/bundled/locales/zh-TW/core/user-story-mapping.md +2 -2
- package/bundled/locales/zh-TW/core/verification-oracle.md +2 -2
- package/bundled/locales/zh-TW/core/workflow-enforcement.md +7 -0
- package/bundled/locales/zh-TW/core/workflow-state-protocol.md +7 -0
- package/bundled/locales/zh-TW/docs/CHEATSHEET.md +8 -1
- package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +29 -68
- package/bundled/locales/zh-TW/docs/FEATURE-REFERENCE.md +25 -15
- package/bundled/locales/zh-TW/docs/MIGRATION-v6.md +8 -4
- package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +1 -2
- package/bundled/locales/zh-TW/integrations/google-antigravity/{INSTRUCTIONS.md → AGENTS.md} +1 -1
- package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +3 -3
- package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +22 -12
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +12 -9
- package/bundled/locales/zh-TW/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/commands/brainstorm.md +17 -13
- package/bundled/locales/zh-TW/skills/commands/config.md +0 -1
- package/bundled/locales/zh-TW/skills/commands/init.md +1 -2
- package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +2 -1
- package/bundled/locales/zh-TW/skills/dev-methodology/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +2 -2
- package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +1 -1
- package/bundled/locales/zh-TW/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/project-discovery/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/release-standards/SKILL.md +3 -0
- package/bundled/locales/zh-TW/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/locales/zh-TW/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/locales/zh-TW/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/skills/atdd-assistant/SKILL.md +2 -0
- package/bundled/skills/bdd-assistant/SKILL.md +2 -0
- package/bundled/skills/brainstorm-assistant/SKILL.md +31 -13
- package/bundled/skills/brainstorm-assistant/guide.md +9 -6
- package/bundled/skills/code-review-assistant/SKILL.md +1 -0
- package/bundled/skills/commands/brainstorm.md +12 -9
- package/bundled/skills/commands/config.md +0 -1
- package/bundled/skills/commands/init.md +2 -3
- package/bundled/skills/commit-standards/SKILL.md +2 -0
- package/bundled/skills/contract-test-assistant/SKILL.md +1 -0
- package/bundled/skills/dev-methodology/SKILL.md +4 -0
- package/bundled/skills/observability-assistant/SKILL.md +1 -0
- package/bundled/skills/project-discovery/SKILL.md +1 -0
- package/bundled/skills/project-structure-guide/SKILL.md +1 -0
- package/bundled/skills/release-standards/SKILL.md +3 -0
- package/bundled/skills/requirement-assistant/SKILL.md +2 -0
- package/bundled/skills/reverse-engineer/SKILL.md +3 -0
- package/bundled/skills/runbook-assistant/SKILL.md +1 -0
- package/bundled/skills/slo-assistant/SKILL.md +1 -0
- package/bundled/skills/tdd-assistant/SKILL.md +2 -0
- package/bundled/templates/.ai-context.yaml.template +194 -0
- package/bundled/templates/CLAUDE.md.template +145 -0
- package/bundled/templates/DESIGN.md +237 -0
- package/bundled/templates/SKILL-BRIEF-TEMPLATE.md +57 -0
- package/bundled/templates/SKILL-CANDIDATES.md +39 -0
- package/bundled/templates/gates/check-error-exit.mjs +309 -0
- package/bundled/templates/mcp-config.json +10 -0
- package/bundled/templates/methodology-template.yaml +209 -0
- package/bundled/templates/migration-template.md +408 -0
- package/bundled/templates/requirement-checklist.md +410 -0
- package/bundled/templates/requirement-document-template.md +591 -0
- package/bundled/templates/requirement-template.md +881 -0
- package/bundled/templates/reverse-spec-template.md +409 -0
- package/bundled/templates/test-case-template.md +74 -0
- package/bundled/templates/test-plan-template.md +74 -0
- package/package.json +12 -9
- package/src/commands/audit.js +82 -0
- package/src/commands/check.js +227 -34
- package/src/commands/config.js +19 -19
- package/src/commands/init.js +174 -20
- package/src/commands/spec.js +2 -2
- package/src/commands/update.js +537 -43
- package/src/compilers/claude-code-compiler.js +4 -1
- package/src/config/ai-agent-paths.js +62 -17
- package/src/core/constants.js +42 -11
- package/src/core/manifest.js +201 -3
- package/src/core/paths.js +2 -2
- package/src/i18n/messages.js +42 -29
- package/src/installers/hooks-installer.js +167 -75
- package/src/installers/integration-installer.js +13 -9
- package/src/installers/skills-installer.js +4 -4
- package/src/installers/standards-installer.js +3 -3
- package/src/prompts/init.js +47 -18
- package/src/reconciler/diff-engine.js +57 -2
- package/src/reconciler/plan-executor.js +31 -11
- package/src/utils/detector.js +21 -1
- package/src/utils/effect-boundary.js +1093 -0
- package/src/utils/hasher.js +166 -1
- package/src/utils/hook-stats.js +1 -1
- package/src/utils/integration-generator.js +288 -82
- package/src/utils/reference-sync.js +107 -8
- package/src/utils/registry.js +57 -0
- package/src/utils/spinner.js +31 -0
- package/src/utils/yaml-generator.js +51 -9
- package/standards-registry.json +32 -9
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* English locale pack for the turn-completion check.
|
|
3
|
+
*
|
|
4
|
+
* Design note — why there is no verb whitelist.
|
|
5
|
+
* The Traditional Chinese detector this was derived from was defeated twice by
|
|
6
|
+
* its own lesson: version one enumerated phrases and missed a phrase; version
|
|
7
|
+
* two enumerated verbs and missed a verb. Swapping a phrase list for a verb
|
|
8
|
+
* list is still enumeration, one layer down.
|
|
9
|
+
*
|
|
10
|
+
* English gets the inverted shape: take whatever word follows the future
|
|
11
|
+
* marker and treat it as an action UNLESS it is in a small, closed list of
|
|
12
|
+
* verbs that describe rather than do. The blacklist can be enumerated honestly
|
|
13
|
+
* because "verbs that are not work" is a bounded set; "verbs that are work" is
|
|
14
|
+
* not.
|
|
15
|
+
*
|
|
16
|
+
* @see core/turn-completion-integrity.md
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Verbs that report, decide-not-to, or legitimately wait. Following one is not a commitment to work. */
|
|
20
|
+
const NON_ACTION = new Set([
|
|
21
|
+
// describing rather than doing
|
|
22
|
+
'say', 'says', 'said', 'note', 'noted', 'mention', 'explain', 'describe',
|
|
23
|
+
'report', 'summarize', 'summarise', 'list', 'show', 'tell', 'clarify',
|
|
24
|
+
'quote', 'point', 'highlight', 'flag', 'call', 'answer', 'respond', 'reply',
|
|
25
|
+
// stating a belief, not an action
|
|
26
|
+
'know', 'think', 'assume', 'believe', 'remember', 'recommend', 'suggest', 'propose',
|
|
27
|
+
// a legitimate stop: the next move is not the agent's
|
|
28
|
+
'wait', 'hold', 'pause', 'defer', 'stop', 'skip', 'leave', 'need', 'want', 'require',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/** Reported speech: a reporting verb governing a clause. "when I say I will do X" is narration. */
|
|
32
|
+
const REPORTED_SPEECH =
|
|
33
|
+
/\b(say|says|said|saying|note|noted|noting|mention|mentioned|tell|told|report|reported|write|wrote|claim|claimed|promise|promised)\b\s+(that\s+)?(I|you|we|it|he|she|they)\b/i;
|
|
34
|
+
|
|
35
|
+
const NEGATION = /\b(not|never|no longer|neither|nor)\b/i;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A reporting verb sitting between "I" and the future marker means the sentence
|
|
39
|
+
* describes a commitment rather than making one: "when I say I will do X".
|
|
40
|
+
* Dropping this check let exactly that sentence block on the first corpus run.
|
|
41
|
+
*/
|
|
42
|
+
const REPORTING =
|
|
43
|
+
/\b(say|says|said|saying|note|noted|mention|mentioned|tell|told|report|reported|write|wrote|claim|claimed|promise|promised|explain|explained)\b/i;
|
|
44
|
+
|
|
45
|
+
/** Requests for information from the human. A commitment beside one is conditional. */
|
|
46
|
+
const ASKING =
|
|
47
|
+
/(\bshould I\b|\bdo you want\b|\bwould you like\b|\bwhich (one|file|option)\b|\blet me know\b|\btell me\b|\bgive me\b|\bpaste\b|\bwaiting on you\b|\byour call\b|\bup to you\b|\?)/i;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Expand contractions so the patterns below never have to fight an apostrophe.
|
|
51
|
+
* Both the ASCII and typographic apostrophes appear in real transcripts.
|
|
52
|
+
*/
|
|
53
|
+
export function normalize(text) {
|
|
54
|
+
return text
|
|
55
|
+
.replace(/[‘’]/g, "'")
|
|
56
|
+
.replace(/\bI'll\b/gi, 'I will')
|
|
57
|
+
.replace(/\bI'm\b/gi, 'I am')
|
|
58
|
+
.replace(/\bI've\b/gi, 'I have')
|
|
59
|
+
.replace(/\bwon't\b/gi, 'will not')
|
|
60
|
+
.replace(/\bcan't\b/gi, 'can not')
|
|
61
|
+
.replace(/n't\b/gi, ' not');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const FUTURE = '(?:will|am going to|going to|am about to|about to)';
|
|
65
|
+
// Adverbs and filler between the future marker and the verb it governs.
|
|
66
|
+
const FILLER = "(?:not|never|also|then|now|next|first|just|be|still|already|soon|finally|quickly|\\w+ly)";
|
|
67
|
+
const COMMIT = new RegExp(
|
|
68
|
+
`\\bI\\b([^.!?\\n]{0,10}?)\\b${FUTURE}\\b((?:\\s+${FILLER}\\b|\\s*,)*)\\s+([a-z][a-z-]*)`,
|
|
69
|
+
'gi'
|
|
70
|
+
);
|
|
71
|
+
const LET_ME = /\blet me\b\s+((?:(?:just|now|first|quickly|\w+ly)\s+)*)([a-z][a-z-]*)/gi;
|
|
72
|
+
|
|
73
|
+
/** Does this sentence contain an unconditional first-person commitment to a next action? */
|
|
74
|
+
export function isCommitment(sentence) {
|
|
75
|
+
const s = normalize(sentence);
|
|
76
|
+
|
|
77
|
+
for (const re of [COMMIT, LET_ME]) {
|
|
78
|
+
re.lastIndex = 0;
|
|
79
|
+
let m;
|
|
80
|
+
while ((m = re.exec(s)) !== null) {
|
|
81
|
+
const isLetMe = re === LET_ME;
|
|
82
|
+
const gap = isLetMe ? '' : m[1];
|
|
83
|
+
const filler = isLetMe ? m[1] : m[2];
|
|
84
|
+
const verb = (isLetMe ? m[2] : m[3]).toLowerCase();
|
|
85
|
+
|
|
86
|
+
if (NON_ACTION.has(verb)) continue;
|
|
87
|
+
if (NEGATION.test(gap) || NEGATION.test(filler)) continue;
|
|
88
|
+
if (REPORTING.test(gap)) continue;
|
|
89
|
+
// A reporting verb governing a clause anywhere earlier in the sentence
|
|
90
|
+
// means this is narration about a commitment, not a commitment.
|
|
91
|
+
if (REPORTED_SPEECH.test(s.slice(0, m.index + 2))) continue;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function isAsking(text) {
|
|
99
|
+
return ASKING.test(normalize(text));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The human asking for the turn to end. Deliberately narrow: a false positive
|
|
104
|
+
* here disables the check for the rest of the session, which is worse than a
|
|
105
|
+
* missed block. It must read as an instruction to stop, not a mention of
|
|
106
|
+
* stopping.
|
|
107
|
+
*/
|
|
108
|
+
const STOP_REQUEST =
|
|
109
|
+
/(\blet's (stop|pause|pick this up later)\b|\b(pause|stop) (here|for now|there)\b|\bhold (on|off)\b|\bthat's (enough|it) for (now|today)\b|\b(done|enough) for (now|today)\b|\bwrap (it |this )?up\b|\bcontinue (this )?later\b|\bpick (this|it) up (tomorrow|later)\b|\btake a break\b|\bI'?m (heading|going) (home|out)\b|\bgood ?night\b)/i;
|
|
110
|
+
|
|
111
|
+
export function isStopRequest(text) {
|
|
112
|
+
return STOP_REQUEST.test(normalize(text));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const id = 'en';
|
|
116
|
+
export const label = 'English';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The corpus. This is the pack's contract: it runs in CI, and a pack whose
|
|
120
|
+
* corpus does not pass is not shipped. Cases marked `true` must block; cases
|
|
121
|
+
* marked `false` must let the turn end.
|
|
122
|
+
*/
|
|
123
|
+
export const corpus = [
|
|
124
|
+
[true, 'plain commitment', 'I will update the remaining two files.'],
|
|
125
|
+
[true, 'contraction + filler', "I'll go ahead and rewrite the detector."],
|
|
126
|
+
[true, 'going to', "Next I'm going to run the test suite."],
|
|
127
|
+
[true, 'let me', 'Let me fix the exclusion and re-run the corpus.'],
|
|
128
|
+
[true, 'about to', "I'm about to move on to the next item in the same class."],
|
|
129
|
+
[true, 'negation then a real commitment in the next sentence',
|
|
130
|
+
'I will not touch that config. I will wire the gate into CI next.'],
|
|
131
|
+
[false, 'past tense report', 'I said I would fix it, and the commit is pushed.'],
|
|
132
|
+
[false, 'negation', "I won't be changing that config."],
|
|
133
|
+
[false, 'asking', 'Should I update the remaining files, or leave them?'],
|
|
134
|
+
[false, 'narrating the pattern', 'The hook fires when I say I will do something and then stop.'],
|
|
135
|
+
// Only REPORTED_SPEECH catches this one: the gap between "I" and "will" is
|
|
136
|
+
// empty, so the gap-level reporting check cannot see the "said that".
|
|
137
|
+
// Added after mutation testing showed REPORTED_SPEECH could be deleted with
|
|
138
|
+
// the corpus staying green — an unproven guard is indistinguishable from a
|
|
139
|
+
// dead one.
|
|
140
|
+
[false, 'third-person reported speech', 'You said that I will handle the merge.'],
|
|
141
|
+
[false, 'reporting verb as the action', "I'll explain why the exclusion exists."],
|
|
142
|
+
[false, 'legitimate stop: the next move is the human\'s', 'I will wait for your key before deploying.'],
|
|
143
|
+
[false, 'conditional: asking in the same paragraph',
|
|
144
|
+
'Tell me which file you meant and I will check it.'],
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Messages from the human, and whether each one ends the turn by request.
|
|
149
|
+
* Marked `true` must exempt; marked `false` must NOT — a message that merely
|
|
150
|
+
* mentions stopping is not an instruction to stop.
|
|
151
|
+
*/
|
|
152
|
+
export const stopCorpus = [
|
|
153
|
+
[true, 'plain pause', "Let's pause here, I'm heading home."],
|
|
154
|
+
[true, 'enough for today', "That's enough for today, we can continue later."],
|
|
155
|
+
[true, 'hold on', 'Hold on, I need to step out.'],
|
|
156
|
+
[false, 'mentions stopping but is not one', 'Explain why the hook stops the turn.'],
|
|
157
|
+
[false, 'asks for work', 'Stop using the hardcoded list and walk the registry instead.'],
|
|
158
|
+
[false, 'ordinary instruction', 'Fix the detector and push it.'],
|
|
159
|
+
];
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Traditional Chinese locale pack for the turn-completion check.
|
|
3
|
+
*
|
|
4
|
+
* Ported from a Python implementation that ran for two weeks on two machines
|
|
5
|
+
* and was defeated five times by its own lesson before settling:
|
|
6
|
+
* 1. enumerated phrases -> missed a phrase
|
|
7
|
+
* 2. enumerated verbs -> missed a verb
|
|
8
|
+
* 3. matched its own comment -> comments stripped
|
|
9
|
+
* 4. matched its own backticked examples -> quoting stripped (shared layer)
|
|
10
|
+
* 5. matched a negated clause -> judgement moved to sentence scope
|
|
11
|
+
*
|
|
12
|
+
* The patterns below are the fifth version. Three of the five defects were
|
|
13
|
+
* caught by the corpus before shipping; the two that shipped were the ones no
|
|
14
|
+
* case covered. That asymmetry is the argument for R7.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT ported: a detector for the phrase "要你決定什麼 / 沒有"
|
|
17
|
+
* ("what you need to decide / nothing"). That keys on one project's report
|
|
18
|
+
* template, not on any language, and shipping it would fire never or wrongly
|
|
19
|
+
* for everyone else.
|
|
20
|
+
*
|
|
21
|
+
* @see core/turn-completion-integrity.md
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// A request to REPORT BACK is also a request: "tell me when it's done and I will
|
|
25
|
+
// verify" is conditional, and its precondition is the human's action. Measured
|
|
26
|
+
// 2026-09-09 — the guard blocked exactly that sentence because it only knew how
|
|
27
|
+
// to spot requests for information.
|
|
28
|
+
//
|
|
29
|
+
// Widening this is the dangerous direction (it misses real unkept commitments),
|
|
30
|
+
// so the pattern is narrow: a completion word immediately followed by a
|
|
31
|
+
// report-back word. A bare "tell me" does not qualify.
|
|
32
|
+
const ASKING = new RegExp(
|
|
33
|
+
'(告訴我|給我|貼一段|貼上|提供|你是在哪|在哪裡看到|哪一個|是哪|需要知道' +
|
|
34
|
+
'|請你|麻煩你|等你(裁決|回覆|決定|確認)|要你自己|要你.{0,6}(動手|執行|跑|做)|由你' +
|
|
35
|
+
'|(做完|跑完|試完|裝完|驗完|改完|弄完|完成後|好了|有結果|通了)' +
|
|
36
|
+
'[,,]?.{0,6}(跟我說|告訴我|回報|讓我知道|再說)' +
|
|
37
|
+
// Asking for a one-word reply is the same kind of request: 「回『全甲』就好」.
|
|
38
|
+
// Narrow: 回 + an optional short quoted string + 就好/就可以/就行. A bare 回 does not qualify.
|
|
39
|
+
'|回(一句)?[「"“]?[^」"”\\n]{0,10}[」"”]?[,,]?.{0,2}就(好|可以|行)' +
|
|
40
|
+
// 「看到開始跑就跟我說,我去檢查」 — the precondition words are unbounded, so listing
|
|
41
|
+
// them (看到, 開始跑, ...) would miss the next one. The grammar is what marks it:
|
|
42
|
+
// 就 followed within two characters by a report-back phrase. A bare 就 does not qualify.
|
|
43
|
+
'|就.{0,2}(跟我說|告訴我|回報我|讓我知道)' +
|
|
44
|
+
// 「跟我說一聲,我會到 PC15 上確認」 — the precondition sat in a heading one paragraph up, so
|
|
45
|
+
// the paragraph opens with the request. A report-back phrase, a comma, then 我 at once is
|
|
46
|
+
// "you tell me, then I act". A report-back phrase alone does not qualify.
|
|
47
|
+
'|(跟我說|告訴我|回報我|讓我知道)(一聲)?[,,]\\s*我)'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// First person + future marker + action verb, within one sentence.
|
|
51
|
+
// The window between marker and verb is 24 characters: Chinese objects can be
|
|
52
|
+
// long, and a 6-character window (chosen by feel) missed a real commitment
|
|
53
|
+
// whose verb sat 11 characters away.
|
|
54
|
+
const FUTURE = '(接著|接下來|繼續|下一步|等一下|待會|再|會|要|來|去|先)';
|
|
55
|
+
const ACTION =
|
|
56
|
+
'(做|辦|處理|推進|接手|進行|查|修|補|寫|跑|建|驗|測|實作|落地|完成|開始|送出|合併|部署' +
|
|
57
|
+
'|走|看|讀|確認|釐清|整理|標|記|盤|清|接|換|補上|收尾|重跑|重寫)';
|
|
58
|
+
// A subject's predicate ends where the same subject appears again: the window may not
|
|
59
|
+
// cross another 我. Measured: 「我先判斷成…,因為我用的是標準輸入」 read the 標 of 標準
|
|
60
|
+
// as the first 我's verb.
|
|
61
|
+
const COMMIT = new RegExp(`我([^我]{0,4})${FUTURE}[^我]{0,24}${ACTION}`, 'g');
|
|
62
|
+
|
|
63
|
+
// Between "我" and the future marker: a reporting verb means the sentence
|
|
64
|
+
// describes a commitment ("我說了下一步"), a negation means it declares the
|
|
65
|
+
// opposite ("我不會做").
|
|
66
|
+
const REPORTING = /(說|寫|講|提|記|標|引用|舉例|回報|報告)/;
|
|
67
|
+
const NEGATION = /(不|沒|未|別|無需|毋須|無須)/;
|
|
68
|
+
// A result clause AFTER the match means the step already happened: 「我先確認…,結果發現…」.
|
|
69
|
+
// Only text after the match counts — 「查了一輪結果發現前提不對,我先去確認落點」 is still a commitment.
|
|
70
|
+
const PAST_RESULT = /(結果|才|後來|於是|然後)[^,,。]{0,3}(發現|查到|看到|知道|證實|確認)/;
|
|
71
|
+
|
|
72
|
+
export function isCommitment(sentence) {
|
|
73
|
+
COMMIT.lastIndex = 0;
|
|
74
|
+
let m;
|
|
75
|
+
while ((m = COMMIT.exec(sentence)) !== null) {
|
|
76
|
+
const gap = m[1];
|
|
77
|
+
if (REPORTING.test(gap) || NEGATION.test(gap)) continue;
|
|
78
|
+
if (PAST_RESULT.test(sentence.slice(m.index + m[0].length))) continue;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isAsking(text) {
|
|
85
|
+
return ASKING.test(text);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The human asking for the turn to end. Kept narrow on purpose: a false
|
|
90
|
+
* positive disables the check for the rest of the session.
|
|
91
|
+
*/
|
|
92
|
+
const STOP_REQUEST =
|
|
93
|
+
// 🔴 `先停` 曾寫成裸的,而語料當場抓到「先**停用**那份硬編碼清單」——
|
|
94
|
+
// 一句要求做事的指令被讀成叫我停。這個方向的誤判會把守衛整場關掉,
|
|
95
|
+
// 所以 `停` 後面接得出動詞的字一律排除。
|
|
96
|
+
/(先暫停|暫停一下|先停(?![用止掉住])|停一下|先不要(做|動)|不用繼續|今天(先)?到這|先這樣|收工|下班|我要回家|明天再(說|弄|做)|改天再|先擱著|睡了|晚安)/;
|
|
97
|
+
|
|
98
|
+
export function isStopRequest(text) {
|
|
99
|
+
return STOP_REQUEST.test(text);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const id = 'zh-TW';
|
|
103
|
+
export const label = '繁體中文';
|
|
104
|
+
|
|
105
|
+
export const corpus = [
|
|
106
|
+
[true, '我接著做兩件不需要你的',
|
|
107
|
+
'我接著做兩件不需要你的:先把那幾格作廢的數字在規格裡逐一標掉,再回去處理那份硬編碼的清單。'],
|
|
108
|
+
[true, '動詞在表外:走',
|
|
109
|
+
'我接著往同一個類別的下一個走:那支腳本裡有一行硬編碼的清單,pre-commit 自己就在警告它漏掉兩個。'],
|
|
110
|
+
[true, '受詞很長,動詞隔了 11 個字',
|
|
111
|
+
'我接著把今天這一輪的三個記憶寫進記憶檔——這三個今天各自撞了不只一次。'],
|
|
112
|
+
[true, '否定句旁邊仍有真承諾',
|
|
113
|
+
'那一項我不會動它。我接著把剩下那份清單整理好,然後回報。'],
|
|
114
|
+
[false, '條件式承諾:要資訊才動得了',
|
|
115
|
+
'我需要知道你是在哪一頁看到那個數字的。給我檔名或貼一段,我去查。'],
|
|
116
|
+
[false, '談論承諾,不是做出承諾',
|
|
117
|
+
'本 session 的守衛不用手動重設就已經恢復。下一次我說了下一步卻沒做,它會擋。'],
|
|
118
|
+
[false, '否定句:宣告不做',
|
|
119
|
+
'那個數字會因為引擎自己恢復而變動,與任何改動無關。我不會拿它做任何判斷。'],
|
|
120
|
+
[false, '合法的停止:事情在使用者手上',
|
|
121
|
+
'只剩那一跑要你自己在終端機跑,探測 repo 和判定腳本都備好了。'],
|
|
122
|
+
// 🔴 條件式承諾的第二種形狀:要求回報。前提在使用者手上,所以不是未兌現的承諾。
|
|
123
|
+
[false, '做完跟我說,我再驗',
|
|
124
|
+
'reload 之後不要關掉那個視窗。做完跟我說,我這邊會再獨立驗一次。'],
|
|
125
|
+
[false, '弄完讓我知道',
|
|
126
|
+
'第二行印出 no 才算數。弄完讓我知道,我接著把設定寫回 repo。'],
|
|
127
|
+
[false, '看到開始跑就跟我說,我去檢查——前提在使用者手上',
|
|
128
|
+
'步驟:\n1. 開 workbench 網頁並登入。\n2. 送出一句需求。\n3. 看到開始跑就跟我說,我去 PC15 檢查。'],
|
|
129
|
+
[false, '跟我說一聲,我會到 PC15 上確認——請求在段首',
|
|
130
|
+
'跟我說一聲,我會到 PC15 上確認三件事:\n- 規格檔在這個專案自己的資料夾裡。\n- workbench 的程式資料夾沒有多出任何檔案。'],
|
|
131
|
+
[true, '「跟我說」是敘述不是請求,後面的承諾仍必須擋',
|
|
132
|
+
'他跟我說過了。我接著去 PC15 確認部署有沒有生效。'],
|
|
133
|
+
[true, '有「就」但沒有要求回報的承諾(仍必須擋)',
|
|
134
|
+
'設定改好就推上去了。我接著去 PC15 檢查部署有沒有生效。'],
|
|
135
|
+
[false, '請使用者回覆「全甲」是條件式',
|
|
136
|
+
'三題都照建議的話,回「全甲」就好。這三題的代號各自不同(丙/甲/甲),不過我會照建議的組合理解。'],
|
|
137
|
+
[true, '回覆請求在另一段,不得豁免這一段的承諾',
|
|
138
|
+
'回「全甲」就好。\n\n我接著把清單整理好。'],
|
|
139
|
+
[false, '窗越過下一個「我」:先判斷成…因為我用的是標準',
|
|
140
|
+
'有一處我自己的判斷一度錯了,要說清楚:lint 報了兩個錯,我先判斷成「原本就壞的」,因為我用的是標準輸入的比對方式,而那個方式給錯答案。'],
|
|
141
|
+
[true, '同一主語內的承諾,後面接另一個「我」也照樣擋',
|
|
142
|
+
'我接著把清單整理好,因為我已經看過了。'],
|
|
143
|
+
[false, '過去的順序敘述:我先確認…,結果發現…',
|
|
144
|
+
'起草之前我先確認這筆設計要落在哪裡,結果發現 DEC-084 的前提有兩處跟程式碼對不上,所以要你決定的事從三題變成四題。'],
|
|
145
|
+
[true, '同一句拿掉結果子句——那就是還沒做的承諾(仍必須擋)',
|
|
146
|
+
'起草之前我先確認這筆設計要落在哪裡。'],
|
|
147
|
+
[true, '結果子句在承諾之前:後半句仍是承諾(仍必須擋)',
|
|
148
|
+
'查了一輪結果發現前提不對,我先去確認落點。'],
|
|
149
|
+
[true, '「發現」出現在承諾的受詞裡,不是結果子句(仍必須擋)',
|
|
150
|
+
'我先去確認有沒有新的發現。'],
|
|
151
|
+
[true, '沒有要求回報的承諾(仍必須擋)',
|
|
152
|
+
'設定檔已經改好了。我接著把驗證結果寫進規格,然後回報。'],
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 使用者的訊息,以及它是不是「叫我停」。
|
|
157
|
+
* 標 true 必須豁免;標 false 必須不豁免——**提到停止不等於叫我停止**。
|
|
158
|
+
*/
|
|
159
|
+
export const stopCorpus = [
|
|
160
|
+
[true, '直接叫停', '先暫停, 我要回家了. 我不會退出程式. 就先暫停'],
|
|
161
|
+
[true, '今天到這', '今天先到這,明天再弄。'],
|
|
162
|
+
[true, '收工', '收工吧,我下班了。'],
|
|
163
|
+
[false, '提到停止但不是叫停', '解釋一下這支 hook 為什麼會擋下回合。'],
|
|
164
|
+
[false, '要求做事而句中有停', '先停用那份硬編碼清單,改成走訪註冊表。'],
|
|
165
|
+
[false, '一般指令', '把偵測器修好然後推上去。'],
|
|
166
|
+
];
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* UDS Hook: Commit Message Format Validator
|
|
4
|
+
*
|
|
5
|
+
* Validates commit messages against Conventional Commits format.
|
|
6
|
+
* Exit code: 0 = valid, 1 = invalid
|
|
7
|
+
*
|
|
8
|
+
* Two callers, two contracts:
|
|
9
|
+
* - git hook / CLI: node validate-commit-msg.mjs "feat(scope): msg"
|
|
10
|
+
* or a raw message on stdin. Exit 1 when invalid.
|
|
11
|
+
* - Claude Code PreToolUse(Bash): the hook payload arrives as JSON on stdin.
|
|
12
|
+
* Exit 2 blocks the tool call; anything that is not a
|
|
13
|
+
* `git commit -m` is none of this hook's business.
|
|
14
|
+
*
|
|
15
|
+
* 🔴 It was registered on UserPromptSubmit and read stdin as a raw commit
|
|
16
|
+
* message, so in an adopter's session it ran on EVERY prompt, failed to parse
|
|
17
|
+
* the hook payload as Conventional Commits, and exited 1 every time. Found by
|
|
18
|
+
* check-hook-delivery.ts, which executes each installed hook rather than
|
|
19
|
+
* checking that the file exists.
|
|
20
|
+
*
|
|
21
|
+
* Performance target: < 500ms
|
|
22
|
+
*
|
|
23
|
+
* @see docs/specs/SPEC-HOOKS-001-core-standard-hooks.md (REQ-1)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const VALID_TYPES = [
|
|
27
|
+
'feat', 'fix', 'docs', 'chore', 'test',
|
|
28
|
+
'refactor', 'style', 'perf', 'ci', 'build', 'revert',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const COMMIT_PATTERN = new RegExp(
|
|
32
|
+
`^(${VALID_TYPES.join('|')})(\\(.+\\))?:\\s.+`
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate a commit message against Conventional Commits format.
|
|
37
|
+
* @param {string} msg - The commit message to validate
|
|
38
|
+
* @returns {boolean} true if valid
|
|
39
|
+
*/
|
|
40
|
+
export function validateCommitMessage(msg) {
|
|
41
|
+
if (!msg || typeof msg !== 'string') return false;
|
|
42
|
+
return COMMIT_PATTERN.test(msg.trim());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// CLI mode.
|
|
46
|
+
function complain(msg) {
|
|
47
|
+
console.error(`❌ Invalid commit message format: "${msg}"`);
|
|
48
|
+
console.error(` Expected: <type>(<scope>): <subject>`);
|
|
49
|
+
console.error(` Valid types: ${VALID_TYPES.join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The message of a `git commit -m ...`, or null when this is not one.
|
|
54
|
+
*
|
|
55
|
+
* The command must BE a git commit, not merely mention one: a substring test
|
|
56
|
+
* pulled `fake"` out of `echo "git commit -m fake"`. Global options are allowed
|
|
57
|
+
* between `git` and `commit` (`git -C /tmp/repo commit -m ...`), which a
|
|
58
|
+
* flags-only pattern rejected. Both were caught by this function's own tests.
|
|
59
|
+
*/
|
|
60
|
+
export function commitMessageFrom(command) {
|
|
61
|
+
if (typeof command !== 'string') return null;
|
|
62
|
+
const segment = command
|
|
63
|
+
.split(/&&|\|\||[;|\n]/)
|
|
64
|
+
.find((s) => /^\s*git\b/.test(s) && /\bcommit\b/.test(s));
|
|
65
|
+
if (!segment) return null;
|
|
66
|
+
const quoted = segment.match(/-m\s+(["'])([\s\S]*?)\1/);
|
|
67
|
+
if (quoted) return quoted[2];
|
|
68
|
+
const bare = segment.match(/-m\s+(\S+)/);
|
|
69
|
+
return bare ? bare[1] : null; // -F, heredoc, or no -m: nothing to inspect
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (process.argv[1] && process.argv[1].endsWith('validate-commit-msg.mjs')) {
|
|
73
|
+
const arg = process.argv[2];
|
|
74
|
+
if (arg) {
|
|
75
|
+
if (validateCommitMessage(arg)) process.exit(0);
|
|
76
|
+
complain(arg);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
} else {
|
|
79
|
+
let data = '';
|
|
80
|
+
process.stdin.setEncoding('utf-8');
|
|
81
|
+
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
82
|
+
process.stdin.on('end', () => {
|
|
83
|
+
const raw = data.trim();
|
|
84
|
+
if (!raw) process.exit(0);
|
|
85
|
+
|
|
86
|
+
let payload = null;
|
|
87
|
+
try { payload = JSON.parse(raw); } catch { /* not a hook payload */ }
|
|
88
|
+
|
|
89
|
+
if (payload && typeof payload === 'object') {
|
|
90
|
+
// Claude Code PreToolUse. Silence unless this really is a commit.
|
|
91
|
+
const msg = commitMessageFrom(payload?.tool_input?.command);
|
|
92
|
+
if (msg === null) process.exit(0);
|
|
93
|
+
if (validateCommitMessage(msg)) process.exit(0);
|
|
94
|
+
complain(msg);
|
|
95
|
+
process.exit(2); // 2 is what blocks a tool call
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Legacy: a raw commit message piped in by a git hook.
|
|
99
|
+
if (validateCommitMessage(raw)) process.exit(0);
|
|
100
|
+
complain(raw);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.
|
|
4
|
-
translation_version: 6.
|
|
5
|
-
last_synced: 2026-
|
|
3
|
+
source_version: 6.10.0
|
|
4
|
+
translation_version: 6.10.0
|
|
5
|
+
last_synced: 2026-09-16
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,6 +17,76 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.10.0] - 2026-09-16
|
|
21
|
+
|
|
22
|
+
### 修复
|
|
23
|
+
|
|
24
|
+
- **`uds update --apply` 会忘掉它没有动到的每一个斜杠命令,而 `uds check` 说一切完整。** 一个装有 51 个 OpenCode 命令的项目,三个文件内容漂移:plan 列出那三个,`--apply` 之后 manifest 只剩三条 `commandHashes`——另外 48 个文件仍在磁盘上,却没有任何东西在跟踪它们。同一次 `uds check` 同时打印「Commands: 51 installed」与「✓ All command files intact (3 files)」,而篡改那 48 个之一,两行都不会变(2026-09-16 于干净临时项目在 6.9.0 上实测)。reconciler 在合并新哈希前,会先删掉该工具的所有哈希键——只有在安装器重装了该工具全部命令时才正确,`uds update` 自己的调用点如此,plan 这条路不是。现在改为合并,与 skills 路径一贯的做法相同;plan 标记删除时,只移除该文件那一条。`uds check` 另外列出磁盘上没有任何哈希覆盖的命令文件,两个数字不再能各说各话。
|
|
25
|
+
- **集成文件指向磁盘上不存在的标准,而引用检查说它们同步。** 以 `--format ai` 安装的项目,`uds update --integrations-only` 写出 `Reference: .standards/anti-hallucination.md` 与 `.standards/checkin-standards.md`,这两个文件只存在于 `human` 格式。规则模板一律以 `.md` 书写引用路径,不看实际安装格式;而规则段落位于 UDS 标记之外,标记式更新从不刷新它们——由旧版 CLI 首次写出的文件,在该文件停止发布两个主版本之后,仍留着 `.standards/commit-message-guide.md`。现在无论生成新文件还是更新既有文件,引用路径都会对照本项目实际安装的标准与格式解析;指向未采用标准的引用会被移除,而不是留成死链接。`uds check` 改为报告「文件不存在」的引用,而不是以去掉扩展名的名称比对。只有 UDS 自己发布的标准会被移除:项目自行放进 `.standards/` 的文件,其引用原样保留。
|
|
26
|
+
- **`uds check` 建议执行 `uds update --sync-refs`,而它跑不起来。** manifest 没有 `integrationConfigs` 时 `--sync-refs` 直接中止,而这个键只有 `uds init` 的交互流程会写入——以 `uds init -y` 创建的项目从第一天起就是 `{}`,所以一个全新的 6.9.0 项目,已经处在那段错误信息归咎于「旧版本、手动复制」的状态。`--sync-refs` 现在会从 manifest(integrations、AI 工具、标准、选项)重建配置,而不是拒绝执行;`uds check` 只在它确实可执行时才提它,否则改指 `uds update --integrations-only`。
|
|
27
|
+
- **`uds check` 把已采用的标准报成索引中缺少。** id 对文件名的表只从 `source.ai` 建立,但部分 registry 条目的 `source` 是字符串——`zh-tw-locale` 是 `extensions/locales/zh-tw.md`——于是它安装出的 `.standards/zh-tw.md` 从未被认出,在明明列出该文件的集成文件上显示「67/68 项标准已引用,缺少:zh-tw-locale」。
|
|
28
|
+
- **引用同步把选项文件报成孤儿,并建议 UDS 已不再发布的文件名。** 选项记在 `manifest.options` 而非 `manifest.standards`,于是每个 `.standards/options/*.ai.yaml` 引用都被报成「未在 manifest 中」;而「未引用的标准」清单来自一张手写的 6.0.0 之前文件名表(`git-workflow.md`、`error-code-standards.md`、`project-structure.md`)。现在选项会被认得,清单也只列出本项目实际持有的文件。
|
|
29
|
+
- **`uds check` 把 `AGENTS.md` 列了两次。** Codex 与 OpenCode 共用同一份文件,而检查是逐工具而非逐文件进行。现在每个文件只报告一次,并标明共用它的工具。
|
|
30
|
+
- **七条 `core/*.md` 标准自 6.0.0 起就没有被安装过,而它们一个字都没说([#180](https://github.com/AsiaOstrich/universal-dev-standards/issues/180))。** 它们的 `.ai.yaml` 在 6.0.0 被移除,文档则刻意留在 `core/` 作为采用层的参考——这个决定合理,也记在 `docs/MIGRATION-v6.md` §2 与 `scripts/reference-only-standards.json` 里,但**没有记在读者真正会遇到它的地方**。迁移指南在升级时读一次,`core/` 是持续被读的:走访它来回答「UDS 有哪些标准」的人或 agent 会数到 152 份,其中七份 `uds init` 从未安装过。七份现在都在开头带一段 `<!-- UDS:REFERENCE-ONLY -->` 告示,英文、繁中、简中三份都有,并指向迁移记录与那份机器可读清单。新闸门(`npm run check:reference-only`,已接进 CI 并附两臂自测)从 registry 里每一条 `source.human` 现算出货面,把它与 `core/*.md` 的差集当成 reference-only 集合,要求每一份都要披露——**反过来也要求正在出货的文档不得带着这段告示**,所以一条重新开始出货的标准不会留下一句谎话。用现算而不是比对那七个名字,是为了让下一次缩减范围不会重演同一种沉默。
|
|
31
|
+
- **`--format human` 的安装把 `testing-standards.md` 挂在两个标准 id 下送出,而全覆盖那一份从来没送到。** `full-coverage-testing` 的 registry 条目把 `source.human` 指到 `core/testing-standards.md`,于是 `core/full-coverage-testing.md`——一条活着的标准,最近一次维护是 XSPEC-288——没有任何人装得到,而 human 格式的 manifest 里同一条路径出现两次。`check-registry-completeness.ts` 的 Check 2 看不到它:那支只要 human 或 ai 任一条路径出现在 registry 文字里就算通过,而 ai 那条在。由上面那支闸门首跑时抓到,是七份预期之外的第八份。修正前后各跑一次真正的 `uds init --format human` 验证。
|
|
32
|
+
- **`uds update --sync-refs` 写进去的标准数,`uds check` 当场否认。** manifest 有 73 条标准的项目,`--sync-refs` 把 CLAUDE.md 改成宣告 76 条,`uds check` 随即回「索引宣告 76 条标准,manifest 实际有 73 条」;而 `--integrations-only` 算得出正确数字,于是它看起来像计数错误,其实是**来源错误**。`--sync-refs` 是从 `integrationConfigs[文件名]` 重新生成的,那里面的 `installedStandards` 是安装当时的快照、**没有任何路径会更新它**——跨大版本升级过的项目,它仍列着 6.0.0 就停止发布的标准,而它列什么,文件内容就是什么。现在改为从 manifest 重新生成;旧快照只剩 `outputLanguage` 还有发言权。
|
|
33
|
+
- **已安装标准索引在每个项目都写「options 0」。** `uds init` 把安装清单缩成纯文件名,而下游一律以 `/options/` 这个路径片段判断选项——在 `contentLayout: flat` 下根本没有目录可以还原它。一个有 66 条核心标准与 7 个选项的项目,被告知自己有「73 条(core 73、options 0)」。同一个缩减也让 AGENTS.md 里每个选项文件都被写成 `.standards/<名称>.ai.yaml`,比选项实际安装的位置高一层,于是每一条都是死链接。现在路径完整传递,AGENTS.md 的选项写在 `.standards/options/`,并依项目实际格式而非一律假设 `ai`。
|
|
34
|
+
- **照着迁移指南做,会让 `uds check` 报错,而它给的解法不可能成功。** MIGRATION-v6 §2 说要手动删掉七个已降级的 `.ai.yaml`。删掉之后 `fileHashes` 记录还在,`uds check` 于是把七个文件报成遗失并建议 `uds check --restore`,而那个还原在每一个文件上都失败(「无法判断来源」)——上游自 6.0.0 起就没有来源了。现在,一个 UDS 已不再发布的文件不见了,会与真正的遗失分开报告;`uds update` 会清掉那些记录并逐笔说出清了什么,**包含已经在最新版的项目**。`--prune` 碰不到它们:它删的是走访 `.standards/` 时找得到的文件。
|
|
35
|
+
- **UDS 重新生成了 AGENTS.md,却没有记下自己写了什么。** `uds update --integrations-only` 之后紧接着 `uds check`,会在一个只有 UDS 动过的文件上报 `AGENTS.md(已修改)`。三个写 AGENTS.md 的调用点都记了 `integrationBlockHashes`,没有一个记 `fileHashes`。现在已存在的记录会被更新;manifest 没在追踪的文件仍然不会被纳入追踪。
|
|
36
|
+
- **在 Windows PowerShell 上,跑成功看起来像失败。** 进度消息走 stderr,而 PowerShell 5.1 会把原生命令的任何 stderr 输出包成 `NativeCommandError`——**包括报告成功的那一句**。改动前实测:stdout 9 行、stderr 2 行,两行都是 spinner。47 个 spinner 现在一律写 stdout。
|
|
37
|
+
- **删除计划用同一句话解释三种完全不同的情况。** 「no longer in desired state」同时被打给「上游已移除的标准」「项目自己取消选取的选项」「desired state 根本没有建模的路径」。现在理由会说出是哪一种。`.standards/release-config.yaml` 则完全不再是删除候选:它是 `uds init` 依用户挑的发布模式写出、`uds config` 会改写的文件,删掉它是在丢掉一个设置却自称在对账。
|
|
38
|
+
- **一个 .NET Framework 项目拿到四个命令,其中三个跑不动。** `.csproj` 自 2017 年起同时指涉两套互不兼容的构建系统,而检测只看扩展名:旧式项目拿到 `dotnet build`(以 `MSB4019` 失败)与 `dotnet list package --vulnerable`(对 `packages.config` 项目静静地什么都不报)。旧式项目现在 `build` 给 `msbuild`,其余留白。
|
|
39
|
+
- **每一份生成的指示文件,开头都叫 AI 去读一个永远不会被安装的目录。** 九个工具、每种语言,全都以「**优先**读取 `core/` 中的精简规则」开场。`uds init` 安装到 `.standards/`,从来没有在采用者的项目里建立过 `core/`。同样两句话有 36 份副本、错法一模一样。现在它们指向 `.standards/`,并有一支测试走访生成结果。
|
|
40
|
+
- **`uds update` 把中文的 CLAUDE.md 改写成英文。** `uds init` 依 `display_language` 生成集成文件内容,而每一条重新生成的路径都是从 `output_language`(commit 消息语言,默认 `english`)推导的。现在内容语言依 `display_language`。
|
|
41
|
+
|
|
42
|
+
### 新增
|
|
43
|
+
|
|
44
|
+
- **`uds check` 现在会报出指示文件里提到、但实际不存在的路径。** 先前它只读 `Reference:`/`参考:` 开头的行、而且只看 `.standards/` 路径。UDS 标记之外、由旧版安装留下、之后没有任何重新生成会回头看的散文,把 agent 指向不存在的文件。这支扫描以**文件存不存在**为准而非以路径长相为准,网址里的路径也会被排除。它首跑就找到了本次修正中的两项缺陷。
|
|
45
|
+
|
|
46
|
+
## [6.9.0] - 2026-09-14
|
|
47
|
+
|
|
48
|
+
### 采用者升级注意
|
|
49
|
+
|
|
50
|
+
- **uds update 不再自动删除文件;会列出可移除的 UDS 旧文件,加 --prune 才会删,且只删 UDS 自己写过的文件。原本依赖自动清理文件的流程请加上 --prune。**(#168、#165,详见「修正」)
|
|
51
|
+
- **--content-mode 给了无法识别的值,现在会打印警告并照旧使用 index(6.8.0 也是生成 index 的内容);7.0.0 起会改为直接报错。** `--content-mode full` 已弃用,会解析为 `index` 并附说明。(详见「弃用」)
|
|
52
|
+
- **以 `uds init -y` 安装的 Codex、OpenCode 等非 Claude 工具,技能现在会装进它们自己的目录**(例如 Codex 是 `.agents/skills/`),不再装进 `.claude/skills/`。已有安装运行 `uds update` 会自动迁移。(详见「修正」)
|
|
53
|
+
- **Antigravity 现在会得到 `.agents/skills/` 与 `.agents/AGENTS.md`**,以前是什么都没有、指令文件写成 `INSTRUCTIONS.md`。Antigravity 只在该目录是已登记的项目时才会读取 `.agents/`,`uds init` 会打印这个提示。
|
|
54
|
+
- **6.8.0 及以前 uds init --with-hooks 写进 .claude/settings.json 的 hook 是从未执行的字符串格式,uds update 不会处理 hook,重跑 uds init --with-hooks 也不会删除旧条目与 scripts/hooks/*.js。要获得可用的 hook,请重跑 uds init --with-hooks,并可手动删除旧的 .js 条目。**
|
|
55
|
+
|
|
56
|
+
### 修正
|
|
57
|
+
|
|
58
|
+
- **`uds update` 不再靠「查一张哈希表」来决定文件是谁的(XSPEC-384,issue [#165](https://github.com/AsiaOstrich/universal-dev-standards/issues/165) 与 [#168](https://github.com/AsiaOstrich/universal-dev-standards/issues/168))。** 两位使用者对同一支命令报告了方向相反的缺陷:它删掉了使用者手写放在 `.standards/` 下的项目文件,没有警告、没有 diff、没有询问、没有备份(#168,是逐行比对 `git status` 才发现的);同时它把 registry 已经移除的标准留在 manifest 与磁盘上,而 `upstream.version` 照样前进到「最新」(#165)。两者都源自同一个布尔值在回答两个彼此独立的问题——**这个文件是不是 UDS 写的**、**它现在还是不是现行标准**——而每个 issue 各撞上这个超载轴的一端。所有权现在改成在 UDS 写入的当下记录,存进 manifest 新增的 `provenance` 字段,并以复制动作实际返回的路径为键,而不是从来源文件名重新推算的路径(正是那个重新推算,让 `fileHashes` 里出现五条 `.standards/options/<name>.ai.yaml`,而真正的文件住在 `options/<category>/<name>.ai.yaml`)。provenance 是只增的,`fileHashes` 是当前状态;若两者同进同退,provenance 会在同一刻一起失效,也就毫无作用。删除现在必须两件事同时成立,其余每一种组合一律保留,**并打印保留的理由**。无法判定所有权时归向保留——那正是旧程序不肯选的那一边。既有安装没有 provenance,因此首次升级无从归属、也不会删任何东西;provenance 只在一次完整写入全套标准的执行结束时才建立。删除另外需要新的 `--prune` 标志,刻意不用 `--yes`:`--yes` 会被设在 CI 里,让它一并授权删除,等于正好在没有人看得到的地方把破坏性路径打开。「默认就删」的分阶段策略放在单一具名常量(`PRUNE_POLICY`),因为 XSPEC-384 §4 尚未裁决。每一份删除报告都打印分母——检查了几个、我方拥有几个、非我方几个、判不了几个、排除几个以及为什么——因为一份只列出动作对象的报告,与「它只看到这几个」无从分辨。两个 issue 都由先证明会红的测试钉住。
|
|
59
|
+
- **九个工具里有八个,技能被装进它们不会读的目录。** 非交互路径(`uds init -y`)下,除 Claude Code 以外每个能装技能的工具,技能都被写进 `.claude/skills/`;一个 codex 仓库执行 `--mode skills -y` 会得到零个技能、零条提示。现在「能否装技能」与「是否走 marketplace」都改为查询路径表;检测到但没有经过验证路径的工具什么都不装,并且会给出提示;`uds update` 转换旧 manifest 时会重新推导工具,不再假设 `claude-code`。Codex 那条未经验证的 `fallbackSkillsPath: '.claude/skills/'` 已移除。交互流程从未受影响。
|
|
60
|
+
- **Roo Code 永远装不上。** 路径表里有它,检测器里没有;它的标识符在一张表里叫 `roo`、其他地方都叫 `roo-code`,于是 `uds init` 把指令写进根目录的 `roo-code.md`。检测改用 `.roo/` 与 `.roorules`(刻意不用 `.clinerules`,那是 Cline 自己的标记);技能也不再退回 `.claude/skills/`,因为 Roo Code 文档列出的位置里没有它。依据是 Roo Code 的文档,未在真实的 Roo Code 上实测。
|
|
61
|
+
- **Antigravity:UDS 六个月来什么都没为它安装,而且写的指令文件是它不会读的那一份(XSPEC-355 OQ6)。** 技能改装进 `.agents/skills/`、指令改写进 `.agents/AGENTS.md`,由工具的二进制文件、随附说明文档、以及一次带诱饵路径的实跑三方确认。它的 tier 从 `minimal` 升为 `partial`。
|
|
62
|
+
- **UDS 至今发布的每一个 hook 都是失效的,而采用者拿到四个坏掉的 hook。** hook 条目写成裸字符串(只有对象形式会运行);`scripts/hooks` 没有进入 npm 包;hook 是 ESM `.js`,在 `"type": "commonjs"` 下会失败(改为 `.mjs`);commit message 校验挂在 `UserPromptSubmit`,每输入一句话就运行一次、每次 exit 1(改挂 `PreToolUse(Bash)`,只检查真正的 `git commit -m`)。
|
|
63
|
+
- **`uds check` 的还原会用一份过期模板覆盖生成的集成文件。** Claude Code 那份是 140 行、最后修改于 2026-03-25 的文件。还原现在改用安装器记录的配置重新生成,受管区块外的内容不动;Antigravity 与 Gemini CLI 第一次有了还原路径,Codex 的后备来源也不再指向不存在的目录。
|
|
64
|
+
- **Codex/OpenCode 的 `AGENTS.md` 没有披露语句。** 2026-08-18 的披露修正只覆盖了两个生成器中的一个;选择 codex 或 opencode 时,`AGENTS.md` 走的是另一个。两种语言、两种输出格式均已修正。
|
|
65
|
+
- **cline、windsurf、roo-code 与 Antigravity 的集成文件只写「遵循 commit-message 规则」,没有写出规则本身。** 格式与合法类型现在直接写在文件里,与 Codex 那份原本的做法相同。
|
|
66
|
+
|
|
67
|
+
### 新增
|
|
68
|
+
|
|
69
|
+
- **`turn-completion-integrity`——agent 不得在说了下一步之后、没做就结束回合。** 由 Stop hook 在回合结束时执行,而不是写成指令。检测器附英文与 zh-TW 语言包及经过变异测试的语料;它会读取用户的最后一条消息,所以被叫停不会被当作未兑现的承诺;逐项列出「谁卡在谁身上」的清单被识别为合法的结束方式;「做完跟我说,我再验」被视为条件式。`uds init --with-hooks` 现在会在你的语言没有语言包时警告,因为在未列出的语言下,hook 装好了却不可能触发。含 zh-TW 与 zh-CN 翻译。
|
|
70
|
+
- **`deferred-item-exit`——六条标准会产出延后项目,而没有一条说那些项目要去哪(XSPEC-391)。** 一位采用者的一次设计工作产出了一份 ADR 与一份规格,其中带有五个标为「待决定」/「另案处理」/「不在 v1」的项目,而**五项全都没有进入任何追踪系统**。原因不是健忘。对会产出这类项目的六条标准——`adr-standards`、`spec-driven-development`、`retrospective-standards`、`feature-discovery-standards`、`code-review-checklist`、`acceptance-criteria-traceability`——grep `issue|tracker|backlog|task id|work item` 共 13 个命中,而**没有任何一个是出口**:ADR 的 `Technical Story` 是输入参照、retrospective 把 tracker 当指标来源、feature-discovery 把工单当需求输入、AC-traceability 只对外部阻塞的覆盖要求链接,而 code-review 的 8 个命中全是英文单字 "issues",意思是「问题」。每条标准都说明如何把自己的文档写好;文档写完就是终点。新标准把缺少的那个关系陈述一次——延后项目在文档之外有一个可追溯的出口,而文档带着那个出口的识别码——六条产出端标准加上 `brainstorm-assistant` skill 各自取得**一行指向它的指针,而不是一份副本**,因为同一条规则的六份副本会往六个方向腐坏。**载体刻意不指定**:issue、受追踪的 TODO、manifest 的一列、backlog 文件里的一行都符合,判准是「这个项目离开文档了吗」,永远不是「用了哪个工具」。三个发现决定了条文。第一,出口必须在产出该文档的那次变更落地*之后*仍然解析得到——两个观察到的泄漏满足其他所有条件,仍然遗失了项目(待办写进一个会被本次合并关闭的 issue;待办写进出口的留言,被后续留言推出视线)。第二,**链接存在不等于链接有效**:回报者自己的闸门会放行他最强的那个例子——一份写着「与 issue #19 一并决定」的文档,而 #19 里根本没有这件事——所以 DEX-005/006 要求「有链接且内容已验证」与「有链接、内容未验证」永远不得印出同一个绿,因为一个被回报成通过的未知,比一个被回报成未知的未知更糟。第三,锚点是文档的**结构**(ADR 的 `Consequences`、规格的范围外章节、retrospective 的 `Action Items`),不是它的措辞:结构可以穷举,措辞清单不行,所以 DEX-008 要求任何措辞清单都要声明其涵盖率未知,DEX-009 要求任何窗口大小(「3 行以内」)都要载明来历,否则标为未校准。依 DEC-049,这条标准完全写成「必须存在的关系」,绝不写成「必须维持它的机制」——而且它在自己的本文里明白写出 **UDS 不为它提供任何闸门**,因为一条陈述了规则却没有东西运行它的标准,不承认这一点时比承认时更糟。`adr-standards` 另外补上一条它从未陈述过的边界(对它 grep `acceptance criteria|\bAC\b|驗收` 得到**零**命中,对照组 `acceptance-criteria-traceability` 为 84 个命中):**验收标准只在一处维护——SPEC**,ADR 以链接指向它——被拷贝的清单有两个拥有者,其中一个永远不会被更新,实测后果是工作出货后 ADR 里的勾选框全部仍是未勾选,而只读规格的覆盖率工具看不到它。
|
|
71
|
+
- **`AUTH_SCOPE_EXTRACTION`——租户界限是一个 `WHERE` 子句,而迁移流程里没有任何东西强迫你去找它(`reverse-engineering-standards` 1.3.0,issue [#166](https://github.com/AsiaOstrich/universal-dev-standards/issues/166))。** 一次真实的 PHP → C# 迁移中,一个以租户为界的账号列表 endpoint 在重写过程中弄丢了它的隔离谓词:依角色限缩范围被换成粗糙的「这个账号是不是任何一个已启用的管理员?」检查,于是不论是谁在问,它都返回数据库里的每一个账号。它出货了、通过了所有既有测试(那个方法一个测试都没有),最后是客户反馈「我看到了不该看到的账号信息」才浮现。`test-completeness-dimensions` 的维度 4 早就点名跨租户访问,所以缺的从来不是维度,缺的是一个在迁移当下强迫想起它的步骤。有三个性质让这一类缺陷穿过每一道闸门:那条安全关键的代码因为住在查询里而非请求/响应契约里,读起来就像众多过滤条件之一;基于形状的断言看不见范围,所以「200 加非空列表」在子句被删掉之后照样通过;而代码库别处存在一支共用 scope resolver 会被读成覆盖,同时一个新的 call site 正在不调用它地重写这个查询。新阶段与隐含规则扫描并行,机械化地推导出每一个以操作者自身身份为界的谓词——包含由 ORM global scope、repository 基类、RLS 策略、注入过滤条件的 middleware 在 SQL 之外执行的范围,因为只 grep SQL 会报告一个比实际更小的集合,而在这里更小的集合读起来恰好就像更安全的集合。每个命中都记下 `file:line`、自我绑定与触发分支;oracle 是「每个角色分支、逐 call site 的谓词等价性」,不是输出形状。RE-AUTH-001 要求一个断言另一操作者记录**不存在**的负向测试(附自我检查:在暂存副本里把谓词删掉,若测试还是通过,那它从来就不是在测范围);RE-AUTH-002 则对任何「移植后等价物未经验证」的谓词 block cutover。
|
|
72
|
+
- **写下来的日期现在必须代表某件事(`tech-debt-standards` 1.1.0、`governance-layer` 1.1.0)。** 登记表模板从 1.0.0 起就要求 `Owner` 与 `Target Resolution Date`,而两份标准里没有任何一处说那个日期过了会怎样——对 `core/tech-debt-standards.md` grep `overdue|past due|expired|exceed` 零命中,而对照组 grep `Owner` 有命中,证明那个「查无」是真的、不是查询工具坏掉。更严重的是 `Registry Storage Options` 把「专用电子表格」列为完全合法:一份没有任何程序读得到的表格满足本节其余每一条要求,它没有任何地方是错的——直到日期过了,然后什么都没发生。`tech-debt-standards` 新增**到期处置**——到期时只有三种合法处置(做掉/撤销/延期并写明理由)、TD-EXP-001..006、明文禁止把到期实现成自动延期或自动关闭(两者都让时钟停止,且都不留下停了的痕迹),以及给跑不动检查的团队的合规替代方案**无人看管声明**。每个存储选项现在都带着「在什么条件下它才合法」;电子表格刻意保留,因为它常是出钱的人唯一会打开的格式。`governance-layer` 新增风险接受条款的 `review_by`(`review_by` 已过的接受不是有效条款,pipeline 必须 fail closed,与没有条款时一样)、**待裁决事项**(GOV-PD-001..004——待裁决是一种带时钟、带固定列举标记的受治理状态,不是自由书写的散文)、**汇总报告**(GOV-RPT-001..005——通过/失败/**判不了**分开打印、写出分母与其推导方式、不得合并成单一分数,因为一个「什么都不做」与「把工作做完」得分相同的指标,量的不是那份工作),以及**新鲜度指标**(GOV-FRESH-001..004——指标必须指名自己读的是编辑时间还是对账时间,两者不得共用字段;一份今天刚编辑、从未与现实对过的文件,在其中一个时钟上是零天、在另一个上是无上限)。两份标准都在标准正文里明白写出 UDS 对这些规则不提供任何检查器:最低标准不是「去建一支检查器」,而是「绝不让一条记载在无声中变成无人看管」。
|
|
73
|
+
- **`uds audit --effects` —— 一道去问「这个回报成功的组件,到底做了什么没有」的闸门(XSPEC-383 R8)。** 形状 D:可达、被调用、跑得动、回传 `{ok: true}`,而它什么都没碰。R3 的可达性闸门看不见它——**可达性问的是入边(谁调用它),形状 D 的病灶在出边(它最终碰到什么),方向相反**。根因是**宣称与证据共用同一个作者**:回传值既是「我做了什么」也是唯一的证据,而类型系统验的那个形状,对真实作与空壳**完全同构**。本闸门走访每个宣告的实作的调用图,量它是否触及跨进程边界。**边界集合从 runtime 自读**——由家族根名规则走访 `builtinModules` 展开(`fs`、`child_process`、`net`/`dgram`/`tls`、`http`/`https`/`http2`、`dns`、`worker_threads`),不是手打清单,因此 `fs/promises` 与未来任何子路径都不需要改代码;且**每条规则都印出自己展开了几个**,展开 0 个就是规则过期了。免 import 的全局 API(`fetch`、`WebSocket`、`process.dlopen`)先探测当前 runtime 才纳入,这个 Node 没有的那些也一并印出来。家族**用 glob 宣告**在 `.uds/effect-boundary.json`,**不用文件名清单**——文件名清单在有人加上第七个 adapter 的那一刻就不再为真,而那正是本闸门存在的理由。判定三态:零命中判 RED(家族中位数 > 0 时带「兄弟有对照组」的佐证;整个家族全为零时**组内比较集体沉默**,报告会明说这个 RED 只由绝对零判准支撑);零命中但带着无法分类的外部依赖则是 **UNDECIDABLE 并 exit 2**——判成绿是 fail-open,判成红是诬告。依 R7-b,**宣告 `NOT_IMPLEMENTED` 且从不回传成功形状的实作是合法的,不需要白名单**——这个连言很重要:只认关键字的话,那个字就是每个空壳将来要走的洞,而两者同时出现的文件会被**指名判 RED**。R7-c 抽出字符串拼接产生的域名(模板内插与 `+` 串接一律折成 `*`,所以 `` `https://${env}.api.acme.example` `` 仍可判定,`` `https://api.${tld}` `` 不可判定),对照**来源可插拔**的持有清单:文件、环境变量,或**命令**——那正是接注册商或云账户 DNS zone API 的位置。**宣告了却读不到 → exit 2**,绝不当成「清单为空所以什么都合法」。基线是 **TSV,每列一个到期日**(§7.11.2),格式坏掉的列 exit 2,因为**坏掉的基线不是「没有基线」**。做的过程中它第一次自测就抓到自己的真缺陷:**type-only import 被当成调用图的边**,于是空壳继承了接口文件的边界命中而判绿——类型 import 编译后整行消失,把它算成命中是**凭空多出来的绿灯**。那个案例现在由一支专属对照臂钉住。**它证明不了什么**:碰到的那个边界是不是**对的**那一个——打一发装饰性 log 请求就能过关。那是 R9,本轮未做。UDS 自己没有效果家族,所以不带 config 直接跑会 exit 2,这是设计而非故障;CI 跑的是 `--self-test`,十七臂(五个探针对照组、分母、绿、红、R7-b 滥用、R7-b 豁免、跨文件调用图、边界金丝雀、域名金丝雀、读不到清单、空集合、不可判定、探针失效)在一份为本 repo 自己写的合成语料上各自证明得出来。
|
|
74
|
+
- **一道采用者跑得动的错误出口闸门。** `templates/gates/check-error-exit.mjs` 现在随包发布。`uds check` 会在有 `src/` 却没有这道闸门的项目上报告,但从不写入;`uds update` 会说明它是什么并提议写入,默认答案是**不要**,文件已存在时绝不覆盖。闸门允许一个文件格式化错误信息,第二个出现时才判红;配置好之前一律 exit 2。
|
|
75
|
+
- **`brainstorm-assistant`:判官现在需要心智,不只需要座位(XSPEC-388)。** BQS D4 必须同时具备独立 context 与声明的模型层级才算通过,模型层级只能用 `core/model-selection.md` 的厂商中立标签表达,指名具体厂商模型即违规。CONVERGE 评审提示改写为「目标+约束」。没有移除任何 v3 机制。
|
|
76
|
+
- **`llms.txt` 改为生成,不再手写。** 由 `uds-manifest.json`、`core/`、`skills/`、`integrations/REGISTRY.json` 与 `cli/package.json` 推导,`--check` 在已提交的文件差一个字节时即失败。
|
|
77
|
+
|
|
78
|
+
### 变更
|
|
79
|
+
|
|
80
|
+
- **P1 与 P3 回到探针集,而它们当初被砍的理由,现在成了另一种错误的实例(XSPEC-408)。** 两者都在 2026-07-23 以「基线已通过」为由退役:在没有安装 UDS 的情况下对 Antigravity CLI 1.0.14 运行,模型会先读档再回答(P1),也会主动给出自己的推荐(P3)。这些观察仍然成立;站不住的是退役这个结论。**每一支探针量的是两件事——一个行为与一个声明的标记——而被推翻的只有行为。** P1 的退役纪录自己就写出了这一点(「剩下的是:UDS 要求明确的 `[Source: <path>]` 标注,而基线用的是 markdown 文件链接」),接着却引用 §2.2「探针断言结构,不断言用词」把剩下的部分丢掉。那个读法不可能正确,因为 **P2 正是靠比对四个字面标签存活**,而且是整组里最耐用的探针;§4 自己的总结也早已得出「always-read 层耐用的部分看起来是声明的形式与内部惯例,而不是知识」,而两支声明形式的探针就被砍在它下面几段。§2.2 现在写出它本来就该承载的区分:比对模型碰巧写出的句子,量到的是运气;比对**标准要求模型印出的标记**,量到的是标准有没有送达。P1 与 P3 改写成只问这个较窄的问题,而且两者现在都会在答案*事实正确但没有标记*时明确失败——那正是它们存在要抓的情形。它们的 §2.3 入场基线不花任何一次新运行:留存的 2026-07-23 逐字稿重新计分后,`[Source:` 出现 **0 次**、`[Recommended]` 出现 **0 次**,各自都对照同一个读取器在同一份文件里找到的阳性控制字符串,所以那个 0 是「不存在」,而不是搜索坏掉。`scripts/check-reinstated-probe-baselines.ts` 在每次 CI 运行时重新推导这两个数字——一个读过一次就打进文档的 0 是一枚戳,而这个 repo 已经记录过戳会发生什么事——其 `--self-test` 臂涵盖标记出现(红)、控制字符串缺失(判不了,绝不是绿)与空逐字稿(判不了),三者皆经突变确认。**这件事没有做到的**:提高 n。每支探针一次运行、一个工具、一个前沿模型、重读一遍。`DELTA-TREND.md` 把这次重新计分记在它自己的子表里,并明文禁止并入上方的总表;Codex 那一列加注其 50% 指的是*已退役*的 P1/P3——Codex 没有留存任何 P1/P3 逐字稿,所以不像 Antigravity 有东西可以重新计分。当初让这些探针被退役的那群对象——采用者在 aider 与 continue-dev 下运行的本机与量化模型——至今仍未被量测过,而一支探针的全部价值,就是在那群对象不再需要这条规则时告诉你。复原探针是把传感器装回去;它没有说它会读到什么。
|
|
81
|
+
- **技能:每一个配套文件现在都能从自己的 `SKILL.md` 一跳到达。** 原本有 13 个配套文件(3,193 行)没有任何东西指向它们,另有 56 个只能经由另一个配套文件到达;`skills/` 与各语言树的 168 个配套文件现在全部是一跳,每一条指引都写明文件内容与何时该读。这消除的是结构风险,并不证明配套文件真的会被读取。
|
|
82
|
+
- **发布的 `.ai.yaml` 的 `meta.updated` 改取自源文件的 git 历史**;git 查无记录时直接省略该字段,不再使用手写日期或 transform 执行当天的日期。
|
|
83
|
+
- **`governance-layer`:Enforcement Reality 一节现在也点名较旧的风险接受字段**(`review_by`、`risks_accepted`、`gates_bypassed`)没有任何程序在读取。
|
|
84
|
+
- **依赖:** `chalk` 5.6.2 → 6.0.0、`js-yaml` 5.2.1 → 5.2.3(CLI 运行时);`eslint`、`globals`、`lint-staged`、`tsx`(开发用)。
|
|
85
|
+
- **仓库内部验证(不影响采用者拿到的内容):** 实跑 `uds init` 的采用者指令文件检查;遍历路径表每个工具的安装路径闸门;实际执行已安装 Stop hook 的 hook 投递闸门;比对内容的 bundle parity;skill 触发面闸门;发布戳一致性检查器;翻译 `source_hash` 棘轮;名称提及分类器;STATUS.md 矛盾闸门;本仓库的 `.claude/skills` 副本改为生成;skill 金丝雀仪器(XSPEC-408 R2)及其首批实跑;P7 探针砍除;bump/sync 往返检查移到每次 CI;测试套件不再受机器语言环境影响。
|
|
86
|
+
|
|
87
|
+
### 弃用
|
|
88
|
+
|
|
89
|
+
- **`contentMode: 'full'`——一个既有模式的第二个名字(XSPEC-357 R7)。** manifest schema 接受它、`--content-mode` 提供它、init 提示把它包装成「拿 context 预算换合规」的取舍(「完整嵌入所有规则、约 10-15 KB、合规率最高」),而且每一个 `partial` 层级的工具都被自动指派它。这些一项都没有被实现:生成器对模式的唯一判断是 `=== 'minimal'`,因此 `full` 与 `index` 走同一条分支,**在实测的 24 组(工具 × 语言)组合中写出逐位相同的文件**,且对照组证明该比较确实抓得到差异(`full` 对 `minimal` 在 24 组中全部不同)。它也不可能按宣称的方式运作——`.standards/` 约 248k token,远超过任何集成文件装得下的量。采用者是在两个标签之间选择同一种行为,而那个标签承诺了一个不存在的差异;文档甚至以法规理由建议银行核心系统采用 `full`。**这次弃用对采用者的代价是零**:`--content-mode full` 仍然接受并解析为 `index`(附说明),manifest 中记录的 `full` 会在下次读取时改写为 `index`,而由于两个模式本来就产生相同的字节,**任何项目生成的文件都不会改变**。无法识别的模式仍然解析为 `index`,但现在会打印警告并写明那个值;7.0.0 起会改为直接报错。`check-adopter-instruction-files.ts` 里的 `content-mode=full` 情境——正是它「8 个文件全部逐位相同」的发现促成了这次移除——一并移除,而不是留着拿 `index` 跟 `index` 比、然后永远报告「相同」。
|
|
20
90
|
## [6.8.0] - 2026-08-20
|
|
21
91
|
|
|
22
92
|
### 新增
|
|
@@ -14,7 +14,7 @@ status: current
|
|
|
14
14
|
|
|
15
15
|
Universal Development Standards 是一个语言无关、框架无关的文件化标准框架。它提供:
|
|
16
16
|
|
|
17
|
-
- **核心规范** (`core/`):
|
|
17
|
+
- **核心规范** (`core/`):152 个基础开发标准
|
|
18
18
|
- **AI 技能** (`skills/`):用于 AI 辅助开发的 Claude Code 技能
|
|
19
19
|
- **CLI 工具** (`cli/`):用于采用标准的 Node.js CLI
|
|
20
20
|
- **整合** (`integrations/`):各种 AI 工具的配置
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **语言**: [English](../../README.md) | [繁體中文](../zh-TW/README.md) | 简体中文
|
|
17
17
|
|
|
18
|
-
**版本**: 6.
|
|
18
|
+
**版本**: 6.10.0 | **发布日期**: 2026-09-16 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
|
|
21
21
|
|
|
@@ -76,7 +76,7 @@ npx universal-dev-standards init
|
|
|
76
76
|
<!-- UDS_STATS_TABLE_START -->
|
|
77
77
|
| 类别 | 数量 | 说明 |
|
|
78
78
|
|----------|-------|-------------|
|
|
79
|
-
| **核心标准** |
|
|
79
|
+
| **核心标准** | 152 | 通用开发准则 |
|
|
80
80
|
| **AI Skills** | 55 | 互动式技能 |
|
|
81
81
|
| **斜线命令** | 51 | 快速操作 |
|
|
82
82
|
| **CLI 命令** | 23 | 项目设置与维护 |
|