@dtmd/temper 0.0.4 → 0.0.7

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.
Files changed (91) hide show
  1. package/README.md +10 -12
  2. package/bin/temper.js +52 -0
  3. package/dist/src/assembly.d.ts +32 -16
  4. package/dist/src/assembly.js +11 -10
  5. package/dist/src/builtins.d.ts +206 -41
  6. package/dist/src/builtins.js +258 -54
  7. package/dist/src/claude-code.d.ts +7 -9
  8. package/dist/src/claude-code.js +6 -8
  9. package/dist/src/contract.d.ts +47 -24
  10. package/dist/src/contract.js +35 -9
  11. package/dist/src/declarations.d.ts +42 -101
  12. package/dist/src/declarations.js +375 -47
  13. package/dist/src/emit.d.ts +56 -28
  14. package/dist/src/emit.js +177 -54
  15. package/dist/src/generated/AssemblyFactRow.d.ts +29 -0
  16. package/dist/src/generated/AssemblyFactRow.js +2 -0
  17. package/dist/src/generated/BoundRow.d.ts +14 -0
  18. package/dist/src/generated/BoundRow.js +2 -0
  19. package/dist/src/generated/CharsetRow.d.ts +15 -0
  20. package/dist/src/generated/CharsetRow.js +2 -0
  21. package/dist/src/generated/ClauseRow.d.ts +97 -0
  22. package/dist/src/generated/ClauseRow.js +1 -0
  23. package/dist/src/generated/CollectionAddressRow.d.ts +17 -0
  24. package/dist/src/generated/CollectionAddressRow.js +2 -0
  25. package/dist/src/generated/CollectionEntryRow.d.ts +23 -0
  26. package/dist/src/generated/CollectionEntryRow.js +2 -0
  27. package/dist/src/generated/CollectionEntryWire.d.ts +17 -0
  28. package/dist/src/generated/CollectionEntryWire.js +2 -0
  29. package/dist/src/generated/CountBoundRow.d.ts +13 -0
  30. package/dist/src/generated/CountBoundRow.js +2 -0
  31. package/dist/src/generated/Declarations.d.ts +89 -0
  32. package/dist/src/generated/Declarations.js +1 -0
  33. package/dist/src/generated/DegreeBoundRow.d.ts +15 -0
  34. package/dist/src/generated/DegreeBoundRow.js +1 -0
  35. package/dist/src/generated/EdgeBoundRow.d.ts +13 -0
  36. package/dist/src/generated/EdgeBoundRow.js +2 -0
  37. package/dist/src/generated/EmbeddedMember.d.ts +45 -0
  38. package/dist/src/generated/EmbeddedMember.js +1 -0
  39. package/dist/src/generated/EmbeddedMemberCollectionEntry.d.ts +21 -0
  40. package/dist/src/generated/EmbeddedMemberCollectionEntry.js +1 -0
  41. package/dist/src/generated/FeatureValue.d.ts +23 -0
  42. package/dist/src/generated/FeatureValue.js +1 -0
  43. package/dist/src/generated/Features.d.ts +82 -0
  44. package/dist/src/generated/Features.js +1 -0
  45. package/dist/src/generated/FencedBlock.d.ts +24 -0
  46. package/dist/src/generated/FencedBlock.js +2 -0
  47. package/dist/src/generated/IncludeRow.d.ts +18 -0
  48. package/dist/src/generated/IncludeRow.js +2 -0
  49. package/dist/src/generated/KindFactRow.d.ts +65 -0
  50. package/dist/src/generated/KindFactRow.js +1 -0
  51. package/dist/src/generated/LayoutRegionRow.d.ts +29 -0
  52. package/dist/src/generated/LayoutRegionRow.js +2 -0
  53. package/dist/src/generated/LayoutRow.d.ts +12 -0
  54. package/dist/src/generated/LayoutRow.js +1 -0
  55. package/dist/src/generated/MentionRow.d.ts +17 -0
  56. package/dist/src/generated/MentionRow.js +2 -0
  57. package/dist/src/generated/NestedMemberRow.d.ts +39 -0
  58. package/dist/src/generated/NestedMemberRow.js +1 -0
  59. package/dist/src/generated/Payload.d.ts +25 -0
  60. package/dist/src/generated/Payload.js +1 -0
  61. package/dist/src/generated/PayloadMember.d.ts +31 -0
  62. package/dist/src/generated/PayloadMember.js +2 -0
  63. package/dist/src/generated/RangeBoundRow.d.ts +15 -0
  64. package/dist/src/generated/RangeBoundRow.js +2 -0
  65. package/dist/src/generated/RegistrationRow.d.ts +35 -0
  66. package/dist/src/generated/RegistrationRow.js +2 -0
  67. package/dist/src/generated/RequirementRow.d.ts +40 -0
  68. package/dist/src/generated/RequirementRow.js +1 -0
  69. package/dist/src/generated/SatisfiesRow.d.ts +14 -0
  70. package/dist/src/generated/SatisfiesRow.js +2 -0
  71. package/dist/src/generated/Section.d.ts +21 -0
  72. package/dist/src/generated/Section.js +2 -0
  73. package/dist/src/generated/SectionContainsRow.d.ts +14 -0
  74. package/dist/src/generated/SectionContainsRow.js +2 -0
  75. package/dist/src/generated/SettingsRow.d.ts +25 -0
  76. package/dist/src/generated/SettingsRow.js +2 -0
  77. package/dist/src/generated/ValueType.d.ts +9 -0
  78. package/dist/src/generated/ValueType.js +2 -0
  79. package/dist/src/generated/index.d.ts +32 -0
  80. package/dist/src/generated/index.js +1 -0
  81. package/dist/src/index.d.ts +14 -18
  82. package/dist/src/index.js +10 -13
  83. package/dist/src/kind.d.ts +190 -43
  84. package/dist/src/kind.js +34 -30
  85. package/dist/src/needs.d.ts +8 -8
  86. package/dist/src/needs.js +7 -7
  87. package/dist/src/prose.d.ts +75 -28
  88. package/dist/src/prose.js +0 -0
  89. package/package.json +9 -1
  90. package/dist/src/genres.d.ts +0 -38
  91. package/dist/src/genres.js +0 -24
@@ -1,28 +1,60 @@
1
1
  /**
2
- * The built-in Claude Code kinds — the face nouns a harness author imports
3
- * (`specs/architecture/15-kinds.md`, "Built-in and custom kinds"). Each is an
2
+ * The built-in Claude Code kinds — the face nouns a harness author imports.
3
+ * Each is an
4
4
  * ordinary `kind<T>()` value built with the same constructor every provider uses
5
5
  * (ownership not privilege). Their five facts are external facts about the Claude
6
6
  * Code harness, cited at the point of claim.
7
7
  *
8
8
  * These are the SDK's own provider-face exports, surfaced through the
9
- * `@dtmd/temper/claude-code` subpath (`50-distribution.md`, "Decision: one SDK
10
- * package — the provider face is a subpath export") — never from the root.
9
+ * `@dtmd/temper/claude-code` subpath
10
+ * — never from the root.
11
11
  */
12
12
  import { kind } from "./kind.js";
13
- import { allowedChars, clause, deny, forbiddenKeys, maxLen, maxLines, minLen, nameMatchesDir, required, } from "./contract.js";
13
+ import { allowedChars, clause, deny, enumOf, forbiddenKeys, maxLen, maxLines, minLen, nameMatchesDir, required, uniqueName, } from "./contract.js";
14
14
  /**
15
15
  * `skill` — `.claude/skills/<name>/SKILL.md`, a directory unit, YAML frontmatter
16
- * carrying `name` then `description`; registers a description trigger
16
+ * carrying `name` then `description`; registers on both documented invocation
17
+ * channels — user-invoked (`/name`) and description-trigger — modulated per
18
+ * member by the `disable-model-invocation`/`user-invocable` fields
17
19
  * (code.claude.com/docs/en/skills, agentskills.io/specification, retrieved
18
- * 2026-07-02).
20
+ * 2026-07-07).
19
21
  */
20
22
  export const skill = kind({
21
23
  name: "skill",
22
24
  locus: { kind: "at", root: ".claude/skills", glob: "*/SKILL.md" },
23
25
  format: "yaml-frontmatter",
24
26
  unitShape: "directory",
25
- registration: { via: "description-trigger", field: "description" },
27
+ registration: [{ via: "user-invoked" }, { via: "description-trigger", field: "description" }],
28
+ identityField: "name",
29
+ });
30
+ /**
31
+ * `command` — `.claude/commands/*.md`, the skill surface's legacy file placement
32
+ * (Claude Code merged commands into skills; code.claude.com/docs/en/skills,
33
+ * retrieved 2026-07-07): a lone file (identity from the stem, so no
34
+ * `identityField` — like `rule`), the skill's field schema by import, registering
35
+ * on the same two documented invocation channels as `skill`.
36
+ */
37
+ export const command = kind({
38
+ name: "command",
39
+ locus: { kind: "at", root: ".claude/commands", glob: "*.md" },
40
+ format: "yaml-frontmatter",
41
+ unitShape: "file",
42
+ registration: [{ via: "user-invoked" }, { via: "description-trigger", field: "description" }],
43
+ });
44
+ /**
45
+ * `agent` — every markdown file under `.claude/agents`, discovered recursively (a
46
+ * containing subdirectory is purely organizational), YAML frontmatter carrying
47
+ * `name` then `description`; identity is the `name` field (never the filename),
48
+ * the named-field mode; registers on the description-trigger channel only — no
49
+ * user-invoked slash command (code.claude.com/docs/en/sub-agents, retrieved
50
+ * 2026-07-07).
51
+ */
52
+ export const agent = kind({
53
+ name: "agent",
54
+ locus: { kind: "at", root: ".claude/agents", glob: "**/*.md" },
55
+ format: "yaml-frontmatter",
56
+ unitShape: "named-field",
57
+ registration: [{ via: "description-trigger", field: "description" }],
26
58
  identityField: "name",
27
59
  });
28
60
  /**
@@ -35,7 +67,7 @@ export const rule = kind({
35
67
  locus: { kind: "at", root: ".claude/rules", glob: "*.md" },
36
68
  format: "yaml-frontmatter",
37
69
  unitShape: "file",
38
- registration: { via: "paths-match", field: "paths" },
70
+ registration: [{ via: "paths-match", field: "paths" }],
39
71
  });
40
72
  /**
41
73
  * `memory` — a root `<name>.md` (`CLAUDE.md`, `AGENTS.md`), a lone file loaded
@@ -48,14 +80,44 @@ export const memory = kind({
48
80
  name: "memory",
49
81
  locus: { kind: "at", root: ".", glob: "**/CLAUDE.md" },
50
82
  unitShape: "file",
51
- registration: { via: "always" },
83
+ registration: [{ via: "always" }],
52
84
  });
53
85
  /**
54
- * The floor for `skill` — Anthropic's documented skill contract: the Agent
86
+ * `hook` — a `settings.json` `hooks.<Event>` registration member: a fields-only kind (no
87
+ * body slot), its members discovered off the `.claude/settings.json` manifest at the
88
+ * `hooks.<Event>` collection address, keyed by lifecycle event; registers on the `event`
89
+ * channel (code.claude.com/docs/en/hooks, retrieved 2026-07-10). The first manifest kind
90
+ * temper ships — the read side of 0021's manifest-authoring surface.
91
+ */
92
+ export const hook = kind({
93
+ name: "hook",
94
+ locus: { kind: "at", root: ".claude", glob: "settings.json" },
95
+ unitShape: "file",
96
+ registration: [{ via: "event", field: "event" }],
97
+ shape: "fields",
98
+ collectionAddress: { manifest: "settings.json", keyPath: "hooks.<Event>" },
99
+ });
100
+ /**
101
+ * `mcpServer` — a `.mcp.json` `mcpServers.*` registration member: a fields-only kind (no
102
+ * body slot), its members discovered off the `.mcp.json` manifest at the `mcpServers.*`
103
+ * collection address, keyed by server name; registers on the `connection` channel
104
+ * (code.claude.com/docs/en/mcp, retrieved 2026-07-10). The second manifest kind temper
105
+ * ships, and the first whose entries are objects — each server's fields fold into the
106
+ * member the read surfaces.
107
+ */
108
+ export const mcpServer = kind({
109
+ name: "mcp-server",
110
+ locus: { kind: "at", root: ".", glob: ".mcp.json" },
111
+ unitShape: "file",
112
+ registration: [{ via: "connection" }],
113
+ shape: "fields",
114
+ collectionAddress: { manifest: ".mcp.json", keyPath: "mcpServers.*" },
115
+ });
116
+ /**
117
+ * The default contract for `skill` — Anthropic's documented skill contract: the Agent
55
118
  * Skills open standard (agentskills.io), Anthropic's platform upload
56
- * validation, and Claude Code's own docs (`packages/skill.anthropic/PACKAGE.md`,
57
- * the curated authoring reference this migrates verbatim; `10-contracts.md`,
58
- * "named for its source"). All sources retrieved 2026-07-01.
119
+ * validation, and Claude Code's own docs.
120
+ * All sources retrieved 2026-07-09.
59
121
  *
60
122
  * Checks the strictest documented profile: the spec and upload validation are
61
123
  * hard, Claude Code's runtime is deliberately forgiving ("All fields are
@@ -67,7 +129,7 @@ export const memory = kind({
67
129
  * description actually triggers well or reads third-person (semantic);
68
130
  * vagueness/no-op detection (semantic); gerund naming (judgment). Two
69
131
  * decidable spec rules are also absent, pending a vocabulary addition (a
70
- * narrow shape predicate — `10-contracts.md` governs additions): the name
132
+ * narrow shape predicate governs additions): the name
71
133
  * must not start/end with a hyphen or contain consecutive hyphens; likewise
72
134
  * the platform's "no XML tags in the description."
73
135
  *
@@ -78,73 +140,116 @@ export const memory = kind({
78
140
  * that is not a command; `metadata` is the sanctioned home for versioning —
79
141
  * there is no top-level `version` field.
80
142
  */
81
- export const skillFloor = [
143
+ export const skillDefaultContract = [
82
144
  clause(required("name"), {
83
145
  severity: "required",
84
146
  guidance: "Every skill declares a `name` — the slug the harness binds to. Claude Code alone would default it from the directory name, but a nameless skill is not portable: the spec and Anthropic's upload validation both require it.",
85
- cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-01)",
147
+ cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-09)",
86
148
  }),
87
149
  clause(minLen("name", 1), {
88
150
  severity: "required",
89
151
  guidance: "A present-but-empty name fails the spec's 1-64 character bound.",
90
- cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-01)",
152
+ cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-09)",
91
153
  }),
92
154
  clause(allowedChars("name", { ranges: ["a-z", "0-9"], chars: "-" }), {
93
155
  severity: "required",
94
156
  guidance: "Lowercase letters, digits, and hyphens only — `PDF-Processing` is the spec's own counter-example. The charset also keeps XML out of the name, which Anthropic's upload validation separately forbids.",
95
- cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-01)",
157
+ cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-09)",
96
158
  }),
97
159
  clause(maxLen("name", 64), {
98
160
  severity: "required",
99
161
  guidance: "Keep the name short and slug-like; it becomes a directory and an id.",
100
- cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-01)",
162
+ cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-09)",
101
163
  }),
102
164
  clause(deny("name", ["anthropic", "claude"]), {
103
165
  severity: "required",
104
166
  guidance: "Reserved words, enforced by Anthropic's platform upload validation (not by the open spec, and not by Claude Code's runtime — which itself ships a `claude-api` skill). Keep them out if the skill will ever travel through the API or claude.ai.",
105
- cite: "https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview#skill-structure (retrieved 2026-07-01)",
167
+ cite: "https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview#skill-structure (retrieved 2026-07-09)",
106
168
  }),
107
169
  clause(nameMatchesDir(), {
108
170
  severity: "required",
109
171
  guidance: "The spec requires the name to match its parent directory. Claude Code decouples the two (the frontmatter name is a display label; the directory names the slash command, except for a plugin-root SKILL.md) — but a mismatch is a portability trap and a reader trap even where it loads.",
110
- cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-01)",
172
+ cite: "https://agentskills.io/specification#name-field (retrieved 2026-07-09)",
111
173
  }),
112
174
  clause(required("description"), {
113
175
  severity: "required",
114
176
  guidance: "The description is how the model chooses this skill from potentially 100+ available — it is the skill's API. Claude Code would fall back to the body's first paragraph; the spec and upload validation require it declared.",
115
- cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-01)",
177
+ cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-09)",
116
178
  }),
117
179
  clause(minLen("description", 1), {
118
180
  severity: "required",
119
181
  guidance: "Say both what the skill does and when to use it, with the keywords a user would naturally say. Write in third person — the text is injected into the system prompt, and inconsistent point-of-view causes discovery problems.",
120
- cite: "https://agentskills.io/specification#description-field (retrieved 2026-07-01)",
182
+ cite: "https://agentskills.io/specification#description-field (retrieved 2026-07-09)",
121
183
  }),
122
184
  clause(maxLen("description", 1024), {
123
185
  severity: "required",
124
186
  guidance: "The spec's cap. Claude Code additionally truncates the skill listing at 1,536 combined characters (description + when_to_use) — truncation, not rejection, but text past the fold cannot help the model choose.",
125
- cite: "https://agentskills.io/specification#description-field (retrieved 2026-07-01)",
187
+ cite: "https://agentskills.io/specification#description-field (retrieved 2026-07-09)",
126
188
  }),
127
189
  clause(maxLen("compatibility", 500), {
128
190
  severity: "required",
129
191
  guidance: "Optional field; when present the spec caps it at 500 characters. Most skills do not need it.",
130
- cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-01)",
192
+ cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-09)",
131
193
  }),
132
194
  clause(maxLines(500), {
133
195
  severity: "advisory",
134
196
  guidance: "Progressive disclosure: keep SKILL.md under 500 lines and move detailed reference material to separate files, one level deep. Once a skill loads, its body stays in context across turns — every line is a recurring token cost. The context window is a public good.",
135
- cite: "https://agentskills.io/specification#progressive-disclosure (retrieved 2026-07-01)",
197
+ cite: "https://agentskills.io/specification#progressive-disclosure (retrieved 2026-07-09)",
136
198
  }),
137
199
  clause(forbiddenKeys(["globs", "alwaysApply"]), {
138
200
  severity: "required",
139
201
  guidance: "Cursor `.mdc` keys. Nothing in the Agent Skills spec or Claude Code's documented frontmatter accepts them — a skill authored with them is carrying dead configuration that another tool's semantics silently fail to apply.",
140
- cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-01)",
202
+ cite: "https://agentskills.io/specification#frontmatter (retrieved 2026-07-09)",
141
203
  }),
142
204
  ];
143
205
  /**
144
- * The floor for `rule` — Anthropic's documented contract for a Claude Code
206
+ * The default contract for `command` — `skillDefaultContract`'s clauses minus `nameMatchesDir`: a
207
+ * command is a lone file with no parent directory to match, so the one clause
208
+ * that ranges over the directory relationship does not apply; every other
209
+ * documented skill-schema recommendation, name-requiredness included, still
210
+ * governs a command by the same import (code.claude.com/docs/en/skills,
211
+ * retrieved 2026-07-07).
212
+ */
213
+ export const commandDefaultContract = skillDefaultContract.filter((entry) => entry.predicate.key !== "name-matches-dir");
214
+ /**
215
+ * The default contract for `agent` — Anthropic's documented subagent contract
216
+ * (code.claude.com/docs/en/sub-agents, retrieved 2026-07-07): `name` and
217
+ * `description` are the only required fields, `name` is a "unique identifier
218
+ * using lowercase letters and hyphens" (no digits, unlike a skill's `name`), and
219
+ * "keep `name` values unique across the whole tree" — a same-scope collision
220
+ * loads only one definition.
221
+ *
222
+ * Deliberately narrow, like `ruleDefaultContract`: undecidable properties (whether the
223
+ * description triggers well, model/permissionMode's semi-open vocabularies) stay
224
+ * out of the gate — the format documents little else that is decidable.
225
+ */
226
+ export const agentDefaultContract = [
227
+ clause(required("name"), {
228
+ severity: "required",
229
+ guidance: "Every subagent declares a `name` — its unique identifier. Claude Code binds identity to this field alone, never the filename, so a nameless subagent cannot be delegated to.",
230
+ cite: "https://code.claude.com/docs/en/sub-agents (retrieved 2026-07-07)",
231
+ }),
232
+ clause(allowedChars("name", { ranges: ["a-z"], chars: "-" }), {
233
+ severity: "required",
234
+ guidance: "Lowercase letters and hyphens only — no digits, unlike a skill's `[a-z0-9-]` name. Hooks receive this value as `agent_type`.",
235
+ cite: "https://code.claude.com/docs/en/sub-agents (retrieved 2026-07-07)",
236
+ }),
237
+ clause(uniqueName(), {
238
+ severity: "required",
239
+ guidance: "Keep `name` values unique across the whole tree — when two files in one scope declare the same name, Claude Code loads only one of them, silently shadowing the other.",
240
+ cite: "https://code.claude.com/docs/en/sub-agents (retrieved 2026-07-07)",
241
+ }),
242
+ clause(required("description"), {
243
+ severity: "required",
244
+ guidance: "The description is how Claude decides when to delegate to this subagent — write it so the trigger is unambiguous.",
245
+ cite: "https://code.claude.com/docs/en/sub-agents (retrieved 2026-07-07)",
246
+ }),
247
+ ];
248
+ /**
249
+ * The default contract for `rule` — Anthropic's documented contract for a Claude Code
145
250
  * rules file, sourced from the memory docs (`.claude/rules/` landed in
146
251
  * v2.0.64; `packages/rule.anthropic/PACKAGE.md`, the curated authoring
147
- * reference this migrates verbatim). All sources retrieved 2026-07-01.
252
+ * reference this migrates verbatim). All sources retrieved 2026-07-09.
148
253
  *
149
254
  * `paths` is the one documented frontmatter key for rules: glob patterns
150
255
  * (brace expansion supported) that scope the rule to matching files. Rules
@@ -152,9 +257,9 @@ export const skillFloor = [
152
257
  * rules load when Claude reads a matching file. Note skills now take a
153
258
  * `paths` key too — the two schemas are separate. (Guidance only: an
154
259
  * optional field asserts nothing decidable, so it carries no clause of its
155
- * own — `10-contracts.md`, "Judged at the node scope": `required` is the one
260
+ * own: `required` is the one
156
261
  * presence predicate, and its absence is not itself a predicate.)
157
- * https://code.claude.com/docs/en/memory#path-specific-rules (retrieved 2026-07-01)
262
+ * https://code.claude.com/docs/en/memory#path-specific-rules (retrieved 2026-07-09)
158
263
  *
159
264
  * What the clauses cannot carry, as guidance: keep a rule to facts Claude
160
265
  * should hold whenever the rule is in scope — concrete enough to verify ("use
@@ -166,29 +271,28 @@ export const skillFloor = [
166
271
  * behavior drifts, and test a change by watching whether Claude's behavior
167
272
  * actually shifts.
168
273
  */
169
- export const ruleFloor = [
274
+ export const ruleDefaultContract = [
170
275
  clause(forbiddenKeys(["description", "globs", "alwaysApply"]), {
171
276
  severity: "required",
172
277
  guidance: "Cursor `.mdc` keys. Claude Code's documented rules schema is `paths`-only; a rule authored with Cursor frontmatter is configuration another tool's semantics silently fail to honor — the rule loads, the scoping you meant does not. (That Claude Code ignores unknown keys is observed behavior, not documented contract — the documented schema is the citation.)",
173
- cite: "https://code.claude.com/docs/en/memory#path-specific-rules (retrieved 2026-07-01)",
278
+ cite: "https://code.claude.com/docs/en/memory#path-specific-rules (retrieved 2026-07-09)",
174
279
  }),
175
280
  clause(maxLines(200), {
176
281
  severity: "advisory",
177
282
  guidance: "Unconditional rules are always-on context, paid every session: the docs' size target is under 200 lines per memory file — 'longer files consume more context and reduce adherence.' (Distinct from the hard 200-line/25KB cutoff, which applies only to auto-memory MEMORY.md; rules load in full regardless of length.) For each line ask: would removing it cause Claude to make mistakes? If not, cut it.",
178
- cite: "https://code.claude.com/docs/en/memory#write-effective-instructions (retrieved 2026-07-01)",
283
+ cite: "https://code.claude.com/docs/en/memory#write-effective-instructions (retrieved 2026-07-09)",
179
284
  }),
180
285
  ];
181
286
  /**
182
- * The floor for the qualified `claude-code.memory` kind — Anthropic's
287
+ * The default contract for the qualified `claude-code.memory` kind — Anthropic's
183
288
  * documented contract for a project `CLAUDE.md` (`packages/memory.anthropic/PACKAGE.md`,
184
- * the curated authoring reference this migrates verbatim). Retrieved 2026-07-02.
289
+ * the curated authoring reference this migrates verbatim). Retrieved 2026-07-09.
185
290
  *
186
291
  * Deliberately near-empty, because the format is: `CLAUDE.md` is plain
187
292
  * markdown with no documented frontmatter and no required fields
188
- * (code.claude.com/docs/en/memory, retrieved 2026-07-02), so there is no
293
+ * (code.claude.com/docs/en/memory, retrieved 2026-07-09), so there is no
189
294
  * schema to gate — manufacturing a required field or a forbidden-key list
190
- * would fake a check the format does not carry (`specs/intent/00-intent.md`,
191
- * law 3: decidable clauses only). The single clause is a context-cost
295
+ * would fake a check the format does not carry. The single clause is a context-cost
192
296
  * budget; everything else the contract could say is guidance.
193
297
  *
194
298
  * What the clauses cannot carry, as guidance: a `paths:` frontmatter block
@@ -206,29 +310,129 @@ export const ruleFloor = [
206
310
  * directory, not below it. Personal, un-shared notes go in `CLAUDE.local.md`
207
311
  * (gitignored), appended after `CLAUDE.md` at its level.
208
312
  */
209
- export const memoryAnthropicFloor = [
313
+ export const memoryAnthropicDefaultContract = [
210
314
  clause(maxLines(200), {
211
315
  severity: "advisory",
212
316
  guidance: "CLAUDE.md is always-on context, paid every session. The memory docs' size target is under 200 lines per memory file — 'longer files consume more context and reduce adherence.' For each line ask: would removing it cause Claude to make mistakes? If not, cut it. (Advisory: Claude Code loads the file in full regardless of length; this is a context-cost budget, not a hard cutoff.)",
213
- cite: "https://code.claude.com/docs/en/memory#write-effective-instructions (retrieved 2026-07-02)",
317
+ cite: "https://code.claude.com/docs/en/memory#write-effective-instructions (retrieved 2026-07-09)",
214
318
  }),
215
319
  ];
216
320
  /**
217
- * The floor for the qualified `agents-md.memory` kind — the AGENTS.md
321
+ * The default contract for the qualified `agents-md.memory` kind — the AGENTS.md
218
322
  * standard's contract for a memory file, which is that there is almost none
219
323
  * (`packages/memory.agents-md/PACKAGE.md`, the curated authoring reference
220
324
  * this migrates). Guidance-only, and that is the honest encoding: `AGENTS.md`
221
325
  * "is just standard Markdown" with no required fields, no sections, and no
222
- * frontmatter (agents.md, retrieved 2026-07-02); the format deliberately
223
- * constrains nothing. A floor that manufactured a required field, a size
326
+ * frontmatter (agents.md, retrieved 2026-07-09); the format deliberately
327
+ * constrains nothing. A default contract that manufactured a required field, a size
224
328
  * gate, or a forbidden-key list would assert a contract the standard
225
- * disclaims (`specs/intent/00-intent.md`, law 3). Even the tempting size
329
+ * disclaims. Even the tempting size
226
330
  * number is a *tool's* rule, not the format's: agents read the closest
227
- * `AGENTS.md` in the tree (nested, nearest-wins); Codex concatenates the
228
- * chain root-to-cwd and stops once combined size hits a byte budget, not a
229
- * per-file line count; Gemini CLI reads `GEMINI.md` by default and only
230
- * treats `AGENTS.md` as an alias when configured; Claude Code does not read
231
- * `AGENTS.md` natively — bridge it with a `CLAUDE.md` that
232
- * `@AGENTS.md`-imports it. All retrieved 2026-07-02.
233
- */
234
- export const memoryAgentsMdFloor = [];
331
+ * `AGENTS.md` in the tree (nested, nearest-wins; agents.md, retrieved
332
+ * 2026-07-09); Codex concatenates the chain root-to-cwd and stops once
333
+ * combined size hits a byte budget, not a per-file line count
334
+ * (`project_doc_max_bytes`, 32 KiB default;
335
+ * developers.openai.com/codex/guides/agents-md, retrieved 2026-07-09);
336
+ * Gemini CLI reads `GEMINI.md` by default and only treats `AGENTS.md` as an
337
+ * alias when configured via `context.fileName` (geminicli.com/docs/cli/gemini-md,
338
+ * retrieved 2026-07-09); Claude Code does not read `AGENTS.md` natively —
339
+ * bridge it with a `CLAUDE.md` that `@AGENTS.md`-imports it
340
+ * (code.claude.com/docs/en/memory, retrieved 2026-07-09).
341
+ */
342
+ export const memoryAgentsMdDefaultContract = [];
343
+ /**
344
+ * Every documented Claude Code hook lifecycle event — the closed set a `hooks.<Event>`
345
+ * key is drawn from (code.claude.com/docs/en/hooks, "Hook events", retrieved 2026-07-10).
346
+ * The allowlist the `hook` default contract's one decidable clause ranges over; the
347
+ * update ritual when the docs add an event is to re-fetch and extend this set, never to
348
+ * re-derive from memory.
349
+ */
350
+ const DOCUMENTED_HOOK_EVENTS = [
351
+ "SessionStart",
352
+ "Setup",
353
+ "UserPromptSubmit",
354
+ "UserPromptExpansion",
355
+ "PreToolUse",
356
+ "PermissionRequest",
357
+ "PermissionDenied",
358
+ "PostToolUse",
359
+ "PostToolUseFailure",
360
+ "PostToolBatch",
361
+ "Notification",
362
+ "MessageDisplay",
363
+ "SubagentStart",
364
+ "SubagentStop",
365
+ "TaskCreated",
366
+ "TaskCompleted",
367
+ "Stop",
368
+ "StopFailure",
369
+ "TeammateIdle",
370
+ "InstructionsLoaded",
371
+ "ConfigChange",
372
+ "CwdChanged",
373
+ "FileChanged",
374
+ "WorktreeCreate",
375
+ "WorktreeRemove",
376
+ "PreCompact",
377
+ "PostCompact",
378
+ "Elicitation",
379
+ "ElicitationResult",
380
+ "SessionEnd",
381
+ ];
382
+ /**
383
+ * The default contract for `hook` — Anthropic's documented hooks contract
384
+ * (code.claude.com/docs/en/hooks, retrieved 2026-07-10). A hook surfaces at
385
+ * `hooks.<Event>`, so the member the gate reads is the lifecycle event itself, its name
386
+ * carried as the `event` field off the collection key. The one decidable, cited property
387
+ * of that member is its event: a key outside the documented set is dead configuration —
388
+ * Claude Code silently never fires a hook under an unrecognized event, so the strictest
389
+ * documented profile is that the event is one temper's cited docs name.
390
+ *
391
+ * Deliberately absent — the handler's own schema (`type`/`command`/`url`/`timeout`, the
392
+ * matcher grammar) lives one array level deeper than `hooks.<Event>`, inside each event's
393
+ * matcher-group list, which the collection address does not walk into; a clause over it
394
+ * would range over a field the read never surfaces, so it is no clause at all. What the
395
+ * clauses cannot carry, as guidance: keep a handler's `type` among
396
+ * `command`/`http`/`mcp_tool`/`prompt`/`agent`; a `command` handler needs a `command`, an
397
+ * `http` handler a `url`; the `matcher` filters tool-scoped events and is inert on events
398
+ * that carry no tool (`UserPromptSubmit`, `Stop`, and their siblings).
399
+ */
400
+ export const hookDefaultContract = [
401
+ clause(enumOf("event", DOCUMENTED_HOOK_EVENTS), {
402
+ severity: "required",
403
+ guidance: "A hook keys under its lifecycle event; an event outside the documented set is dead configuration — Claude Code silently never fires a hook under an unrecognized event. If this is a newly-documented event, re-fetch code.claude.com/docs/en/hooks and extend temper's cited set rather than working around the finding.",
404
+ cite: "https://code.claude.com/docs/en/hooks (retrieved 2026-07-10)",
405
+ }),
406
+ ];
407
+ /**
408
+ * Every documented `.mcp.json` server transport — the closed set a server entry's `type`
409
+ * is drawn from (code.claude.com/docs/en/mcp, retrieved 2026-07-10). `stdio` is the
410
+ * default when `type` is absent; `streamable-http` is the MCP spec's own name for `http`,
411
+ * accepted as an alias so configurations copied from server docs work unchanged; `sse` is
412
+ * documented but deprecated; `ws` is the WebSocket transport. The update ritual when the
413
+ * docs add a transport is to re-fetch and extend this set, never to re-derive from memory.
414
+ */
415
+ const DOCUMENTED_MCP_TRANSPORTS = ["stdio", "http", "streamable-http", "sse", "ws"];
416
+ /**
417
+ * The default contract for `mcpServer` — Anthropic's documented `.mcp.json` contract
418
+ * (code.claude.com/docs/en/mcp, retrieved 2026-07-10). A server surfaces at `mcpServers.*`,
419
+ * keyed by name, its transport-specific fields folded into the member. The one decidable,
420
+ * cited property that holds across every transport is `type`: a value outside the
421
+ * documented set is a transport Claude Code cannot honor, so the strictest documented
422
+ * profile is that a present `type` names one temper's cited docs carry. An absent `type`
423
+ * passes — Claude Code reads it as `stdio`, the documented default.
424
+ *
425
+ * Deliberately absent — the per-transport requirements are conditional on `type`, which no
426
+ * single-field clause can decide: a `url` with no `type` is a configuration error (Claude
427
+ * Code reads it as a stdio server and skips it), a stdio server needs a `command`, and a
428
+ * remote server needs a `url` — each a two-field implication the closed predicate
429
+ * vocabulary cannot express, so it rides guidance rather than a clause that would range
430
+ * over a field the shape of the check cannot see.
431
+ */
432
+ export const mcpServerDefaultContract = [
433
+ clause(enumOf("type", DOCUMENTED_MCP_TRANSPORTS), {
434
+ severity: "required",
435
+ guidance: "A server's `type` names its transport; a value outside the documented set is one Claude Code cannot honor. Absent reads as `stdio` — but an entry that carries a `url` with no `type` is then a configuration error, because Claude Code treats it as a stdio server and skips it: add `type: \"http\"` (or `sse`/`ws`). A stdio server needs a `command`; a remote server needs a `url`. If this is a newly-documented transport, re-fetch code.claude.com/docs/en/mcp and extend temper's cited set rather than working around the finding.",
436
+ cite: "https://code.claude.com/docs/en/mcp (retrieved 2026-07-10)",
437
+ }),
438
+ ];
@@ -1,15 +1,13 @@
1
1
  /**
2
2
  * The `@dtmd/temper/claude-code` subpath — the first-party Claude Code
3
- * provider face (`specs/architecture/50-distribution.md`, "Decision: one SDK
4
- * package — the provider face is a subpath export"). A harness author who
3
+ * provider face. A harness author who
5
4
  * targets Claude Code imports the built-in kinds from here, never the root:
6
- * the root carries only the six-noun core, and identity travels by import
7
- * (`specs/architecture/15-kinds.md`), so a subpath specifier is a full module
8
- * specifier like any other. The built-in floors (exported clause arrays,
9
- * `10-contracts.md`, "A shared clause set — a floor") join the kinds here
10
- * too: adoption is `import { skill, skillFloor } from "@dtmd/temper/claude-code"`.
5
+ * the root carries only the six-noun core, and identity travels by import,
6
+ * so a subpath specifier is a full module
7
+ * specifier like any other. The built-in default contracts join the kinds here
8
+ * too: adoption is `import { skill, skillDefaultContract } from "@dtmd/temper/claude-code"`.
11
9
  */
12
- export type { Memory, Rule, Skill } from "./builtins.js";
13
- export { memory, memoryAgentsMdFloor, memoryAnthropicFloor, rule, ruleFloor, skill, skillFloor } from "./builtins.js";
10
+ export type { Agent, Hook, McpServer, Memory, Rule, Skill } from "./builtins.js";
11
+ export { agent, agentDefaultContract, command, commandDefaultContract, hook, hookDefaultContract, mcpServer, mcpServerDefaultContract, memory, memoryAgentsMdDefaultContract, memoryAnthropicDefaultContract, rule, ruleDefaultContract, skill, skillDefaultContract, } from "./builtins.js";
14
12
  export type { Blocks, File, Prose, Text } from "./prose.js";
15
13
  export { blocks, file, text } from "./prose.js";
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * The `@dtmd/temper/claude-code` subpath — the first-party Claude Code
3
- * provider face (`specs/architecture/50-distribution.md`, "Decision: one SDK
4
- * package — the provider face is a subpath export"). A harness author who
3
+ * provider face. A harness author who
5
4
  * targets Claude Code imports the built-in kinds from here, never the root:
6
- * the root carries only the six-noun core, and identity travels by import
7
- * (`specs/architecture/15-kinds.md`), so a subpath specifier is a full module
8
- * specifier like any other. The built-in floors (exported clause arrays,
9
- * `10-contracts.md`, "A shared clause set — a floor") join the kinds here
10
- * too: adoption is `import { skill, skillFloor } from "@dtmd/temper/claude-code"`.
5
+ * the root carries only the six-noun core, and identity travels by import,
6
+ * so a subpath specifier is a full module
7
+ * specifier like any other. The built-in default contracts join the kinds here
8
+ * too: adoption is `import { skill, skillDefaultContract } from "@dtmd/temper/claude-code"`.
11
9
  */
12
- export { memory, memoryAgentsMdFloor, memoryAnthropicFloor, rule, ruleFloor, skill, skillFloor } from "./builtins.js";
10
+ export { agent, agentDefaultContract, command, commandDefaultContract, hook, hookDefaultContract, mcpServer, mcpServerDefaultContract, memory, memoryAgentsMdDefaultContract, memoryAnthropicDefaultContract, rule, ruleDefaultContract, skill, skillDefaultContract, } from "./builtins.js";
13
11
  export { blocks, file, text } from "./prose.js";
@@ -1,17 +1,16 @@
1
1
  /**
2
- * Contracts — clauses and requirements as typed values (`specs/architecture/10-contracts.md`).
2
+ * Contracts — clauses and requirements as typed values.
3
3
  * A clause is `predicate · severity · guidance · cite`; a requirement is
4
- * `means · kind · required · clauses? · verifiedBy?`. Both erase to compiled data
5
- * at the seam (`20-surface.md`): the author composes typed objects, the engine
4
+ * `prose · kind · required · clauses? · verifiedBy?`. Both erase to compiled data
5
+ * at the seam: the author composes typed objects, the engine
6
6
  * consumes their rows. The predicate vocabulary is the closed algebra — a clause
7
- * outside it is a squiggle, not a runtime rejection (`10-contracts.md`, the two
8
- * walls).
7
+ * outside it is a squiggle, not a runtime rejection.
9
8
  */
10
9
  import type { KindDefinition } from "./kind.js";
11
10
  /** A clause's delivery posture: `required` gate-blocks, `advisory` reports. */
12
11
  export type Severity = "required" | "advisory";
13
12
  /**
14
- * A member of the closed predicate algebra (`10-contracts.md`). Both a kind's
13
+ * A member of the closed predicate algebra. Both a kind's
15
14
  * own `expect` clauses and a requirement's `clauses` compile to the row's full
16
15
  * `key`/`field`/`severity`/argument shape (`declarations.ts` `clauseRow`): a
17
16
  * floor clause's own `bound`/`charset`/`keys`/`values` ride the row alongside
@@ -28,21 +27,29 @@ export interface Predicate {
28
27
  readonly args?: Readonly<Record<string, number>>;
29
28
  /**
30
29
  * `membership`'s target requirement name — a separate slot from `field` (the
31
- * checked field) since `membership` names both (`10-contracts.md`, "Judged
32
- * at the node-set scope").
30
+ * checked field) since `membership` names both.
33
31
  */
34
32
  readonly target?: string;
35
- /** `allowed_chars`'s declared character class (`10-contracts.md`, "allowed_chars"). */
33
+ /** `allowed_chars`'s declared character class. */
36
34
  readonly charset?: Charset;
37
35
  /** `forbidden_keys`'s forbidden key list. */
38
36
  readonly keys?: readonly string[];
39
37
  /** `enum`/`deny`'s permitted or forbidden value list. */
40
38
  readonly values?: readonly string[];
39
+ /** `range`'s inclusive numeric bound. */
40
+ readonly range?: {
41
+ readonly min: number;
42
+ readonly max: number;
43
+ };
44
+ /** `section_contains`'s heading-text prefix and the marker each governed section must carry. */
45
+ readonly section?: {
46
+ readonly heading: string;
47
+ readonly marker: string;
48
+ };
41
49
  }
42
50
  /**
43
51
  * The character class `allowed_chars` admits — inclusive ranges plus individual
44
- * characters, e.g. `[a-z0-9-]` (`10-contracts.md`, "Decision: `allowed_chars`,
45
- * not a general `pattern` clause"). Each range is a `"<lo>-<hi>"` two-character
52
+ * characters, e.g. `[a-z0-9-]`. Each range is a `"<lo>-<hi>"` two-character
46
53
  * span (`src/contract.rs` `parse_range`'s wire spelling).
47
54
  */
48
55
  export interface Charset {
@@ -69,6 +76,23 @@ export declare const deny: (field: string, values: readonly string[]) => Predica
69
76
  export declare const requireSections: () => Predicate;
70
77
  /** The member's name matches its directory. */
71
78
  export declare const nameMatchesDir: () => Predicate;
79
+ /** Names are unique within the artifact kind (a scope-wide identity collision). */
80
+ export declare const uniqueName: () => Predicate;
81
+ /**
82
+ * The named field may be present — always satisfied, recording the key as part of a
83
+ * declared (closed) schema. `dependency-exists` has no constructor: the engine holds
84
+ * it back absent a decidable reference syntax, so a hand-authored clause would fail
85
+ * admissibility.
86
+ */
87
+ export declare const optional: (field: string) => Predicate;
88
+ /** The field's numeric value lies within the inclusive `[min, max]` bound. */
89
+ export declare const range: (field: string, min: number, max: number) => Predicate;
90
+ /** The field's value is one of `values`. Spelled `enumOf` — `enum` is a reserved word. */
91
+ export declare const enumOf: (field: string, values: readonly string[]) => Predicate;
92
+ /** The named body marker is defined (e.g. `disable-model-invocation`). */
93
+ export declare const mustDefine: (marker: string) => Predicate;
94
+ /** Every body section whose heading *starts with* `heading` carries `marker` in its body. */
95
+ export declare const sectionContains: (heading: string, marker: string) => Predicate;
72
96
  /** The satisfier set's size lies in the inclusive `[min, max]` bound. */
73
97
  export declare const count: (bounds: {
74
98
  min?: number;
@@ -92,7 +116,7 @@ export declare const degree: (bounds: {
92
116
  /**
93
117
  * A clause — a predicate the author marks with a severity, the just-in-time
94
118
  * guidance the predicate cannot encode, and the external-fact `cite` that makes
95
- * a maintained floor auditable (`10-contracts.md`, "The clause").
119
+ * a maintained floor auditable.
96
120
  */
97
121
  export interface Clause {
98
122
  readonly predicate: Predicate;
@@ -107,22 +131,21 @@ export declare function clause(predicate: Predicate, opts: {
107
131
  cite?: string;
108
132
  }): Clause;
109
133
  /**
110
- * A requirement — a named obligation on the harness (`10-contracts.md`,
111
- * "Requirements"). `means` is the authored intent, carried never interpreted;
112
- * `kind` constrains what may fill it **by import** (a value, never a string);
113
- * `required` is the posture declaration; `clauses` are the requirement's own
114
- * set-/edge-scope demands — ordinary [`Clause`] values whose predicates range
115
- * over the satisfier set (`count`/`unique`/`membership`) or its graph
116
- * neighborhood (`degree`), the same four-channel clause as everywhere
117
- * (`10-contracts.md`, "Decision: set-scope demands are clauses"); `verifiedBy`
118
- * wires the behavioral remainder.
134
+ * A requirement — a named obligation on the harness. `prose` is the authored
135
+ * intent, carried never interpreted; `kind` constrains what may fill it **by
136
+ * import** (a value, never a string); `required` is the posture declaration;
137
+ * `clauses` are the requirement's own set-/edge-scope demands — ordinary
138
+ * [`Clause`] values whose predicates range over the satisfier set
139
+ * (`count`/`unique`/`membership`) or its graph neighborhood (`degree`), the
140
+ * same four-channel clause as everywhere; `verifiedBy` wires the behavioral
141
+ * remainder.
119
142
  */
120
143
  export interface Requirement {
121
- readonly means: string;
122
- readonly kind?: KindDefinition<object>;
144
+ readonly prose: string;
145
+ readonly kind?: KindDefinition<never>;
123
146
  readonly required?: boolean;
124
147
  readonly clauses?: readonly Clause[];
125
148
  readonly verifiedBy?: string;
126
149
  }
127
- /** An identity helper — types a requirement literal at the keystroke (`40-composition.md`). */
150
+ /** An identity helper — types a requirement literal at the keystroke. */
128
151
  export declare function requirement(init: Requirement): Requirement;