@rungs/cli 0.1.0 → 0.1.2

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.
@@ -33,6 +33,24 @@ non_empty = ["Sev", "Pri", "What", "Evidence"]
33
33
  enum = { Sev = ["high", "medium", "low"], Pri = ["now", "next", "someday"] }
34
34
  placeholder_ok = ["none — hunch", "unknown"]
35
35
 
36
+ # ── findings-self-declared-closure ───────────────────────────────────────────
37
+
38
+ [self_declared_closure]
39
+ file = "{{path}}"
40
+ open_heading = "Open"
41
+ closed_heading = "Closed"
42
+ detail_heading = "Detail"
43
+ id_pattern = "{{id_prefix}}-\\d{1,4}"
44
+ open_row_pattern = "^\\|\\s*\\[?({{id_prefix}}-\\d{1,4})\\]"
45
+ detail_heading_pattern = "^###\\s+({{id_prefix}}-\\d{1,4})\\s+—\\s+"
46
+ exempt_marker = "closure-ok:"
47
+ declares_fixed = [
48
+ "\\*\\*Fixed[.,)*]",
49
+ "\\*\\*Fixed\\s+(?:in|by|the\\s+same\\s+day|\\d{4}-\\d{2}-\\d{2})",
50
+ "\\*\\*Implemented in this change\\.?\\*\\*",
51
+ "\\*\\*fixed in the pass that found it\\*\\*",
52
+ ]
53
+
36
54
  # ── self-tests ────────────────────────────────────────────────────────────────
37
55
 
38
56
  [[self_test]]
@@ -55,6 +73,16 @@ gate = "findings-disposition-has-reason"
55
73
  expect = "pass"
56
74
  fixture = { table = "Open", row = { Id = "{{id_prefix}}-002", Sev = "high", Pri = "now", What = "x", Evidence = "none — hunch" } }
57
75
 
76
+ [[self_test]]
77
+ gate = "findings-self-declared-closure"
78
+ expect = "fail"
79
+ fixture = { open = ["{{id_prefix}}-001"], detail = "### {{id_prefix}}-001 — x\\n\\n**Fixed.**" }
80
+
81
+ [[self_test]]
82
+ gate = "findings-self-declared-closure"
83
+ expect = "pass"
84
+ fixture = { open = ["{{id_prefix}}-001"], detail = "### {{id_prefix}}-001 — x\\n\\n<!-- closure-ok: the mitigation only -->\\n**Fixed.**" }
85
+
58
86
  # ── findings-ids self-tests ───────────────────────────────────────────────────
59
87
 
60
88
  [[self_test]]
@@ -1,6 +1,6 @@
1
1
  [module]
2
2
  name = "findings"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  rung = 1
5
5
  summary = "A register for what is noticed before it is decided, with a triage path into work items."
6
6
 
@@ -45,6 +45,14 @@ A finding closed without a written reason is indistinguishable from one nobody l
45
45
  is the same observation the next session will record again. The reason is what the register is for.
46
46
  """
47
47
 
48
+ [[gates]]
49
+ id = "findings-self-declared-closure"
50
+ kind = "declared"
51
+ engine = "self-declared-closure"
52
+ table = "gates/findings.toml"
53
+ tier = "fast"
54
+ why = "An open finding whose own detail says it is fixed contradicts the register; this narrow text check catches drift without pretending to prove code-level closure."
55
+
48
56
  # ── skills ────────────────────────────────────────────────────────────────────
49
57
 
50
58
  [skills.record-finding]
@@ -70,6 +70,20 @@ gate = "gates-links-resolve"
70
70
  expect = "pass"
71
71
  input = "See [this table](./structural.toml)."
72
72
 
73
+ # WI-008. A `${{token}}` used to exempt the whole file, so a document explaining templating stopped
74
+ # being link-checked entirely — 16 files here, eight of them shipped. These two fix the boundary:
75
+ # a broken link is caught even with a token elsewhere in the file, and a link that is *itself* a
76
+ # template is not.
77
+ [[self_test]]
78
+ gate = "gates-links-resolve"
79
+ expect = "fail"
80
+ input = "Set `${{repo.dirname}}` first, then see [the plan](./does-not-exist.md)."
81
+
82
+ [[self_test]]
83
+ gate = "gates-links-resolve"
84
+ expect = "pass"
85
+ input = "The module writes [its readme](${{path}}/README.md) once installed."
86
+
73
87
  [[self_test]]
74
88
  gate = "gates-paths-exist"
75
89
  expect = "pass"
@@ -11,12 +11,6 @@ procedure belongs in a skill. Both load only when relevant, and this file has a
11
11
 
12
12
  <!-- One paragraph: what the project does, for whom, and the one property that must not break. -->
13
13
 
14
- ## Repo map
15
-
16
- <!-- rungs:begin repo-map -->
17
- <!-- Generated. Run `rungs render` after moving directories. -->
18
- <!-- rungs:end repo-map -->
19
-
20
14
  ## Validation matrix
21
15
 
22
16
  **Run the narrowest validation that covers what you touched**, then the standard gates. Running
@@ -0,0 +1,12 @@
1
+ <!-- rungs:begin instructions@1.1.0 -->
2
+ ## Repo map
3
+
4
+ <!-- rungs:begin repo-map -->
5
+ <!-- Generated. Run `rungs render` after moving directories. -->
6
+ <!-- rungs:end repo-map -->
7
+ ## Execution boundary
8
+
9
+ Before an agent command runs, name the execution unit, what filesystem/environment/credential/network
10
+ surfaces cross into it, and which controls are absent. This repository guidance declares boundaries;
11
+ it does not install a sandbox, transport, rollback, or least-privilege runtime.
12
+ <!-- rungs:end instructions -->
@@ -1,6 +1,6 @@
1
1
  [module]
2
2
  name = "instructions"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  rung = 0
5
5
  summary = "The agent entry point, the harness bridge, path-scoped rule sources, and the render pipeline. The one module every repo installs."
6
6
 
@@ -14,7 +14,11 @@ modules = []
14
14
 
15
15
  [params.project_name]
16
16
  description = "Name used in the entry document's first line"
17
- default = "" # inferred from the repo directory when blank
17
+ default = "{{repo.dirname}}"
18
+ # `repo` is the reserved namespace, not a module — see modules/README.md rule 9b. This default was
19
+ # `""` with a comment saying it was inferred from the directory, which nothing implemented, so every
20
+ # scaffold's first line read `# AGENTS.md — ` with a dangling dash. Stating the inference where the
21
+ # default is read is what makes it checkable (WI-001).
18
22
 
19
23
  [params.harnesses]
20
24
  description = "Which coding agents to emit for. Drives the ADR-0001 render matrix."
@@ -103,7 +107,7 @@ paths = { claude = ".claude/", copilot = ".github/instructions/", cursor = ".c
103
107
  # ── provenance (required, validated) ──────────────────────────────────────────
104
108
 
105
109
  [provenance]
106
- sources = ["axiom-mesh", "hexguard", "hexguard-templates", "rift-forge"]
110
+ sources = ["axiom-mesh", "hexguard", "hexguard-templates", "rift-forge", "openhands"]
107
111
  patterns = [
108
112
  "entry-point",
109
113
  "agents-md-bridge",
@@ -116,6 +120,7 @@ patterns = [
116
120
  "comms-style",
117
121
  "shell-editing-rules",
118
122
  "instruction-hardening",
123
+ "isolation-boundary-declaration",
119
124
  ]
120
125
  incident = """
121
126
  All four source repos converged on a single canonical entry document, and all four put content in
@@ -124,4 +129,7 @@ targets was a dead redirect. hexguard-templates' repo map grew five duplicated e
124
129
  every session reads first, in the repo whose conventions forbid restating scope. rift-forge reached
125
130
  1513 lines with no path scoping at all, while hexguard had solved that with applyTo globs a month
126
131
  earlier. Nobody had both completeness and scope.
132
+ OpenHands adds the missing execution boundary: a repository instruction can name the execution unit,
133
+ the filesystem/environment/credential/network crossings, and the controls it does not supply, but
134
+ it cannot install a sandbox or a rollback guarantee.
127
135
  """
@@ -1,7 +1,8 @@
1
1
  # Session state
2
2
 
3
3
  The live handoff. **Rewritten as work moves, not appended to.** Everything here is present tense;
4
- history belongs in [`{{archive}}/`](archive/README.md).
4
+ history belongs in [`{{archive}}/`](archive/README.md). This is narrative continuity, not a
5
+ checkpoint, event stream, conversation memory, or audit record.
5
6
 
6
7
  Keep it short. This document is read at the start of every session, and a long one is skimmed.
7
8
 
@@ -1,6 +1,9 @@
1
- <!-- rungs:begin session@1.0.0 -->
1
+ <!-- rungs:begin session@1.1.0 -->
2
2
  ## Session state
3
3
 
4
4
  Read [`{{path}}`]({{path}}) first. Treat **Active constraints — do not reopen** as binding:
5
5
  settled decisions, not suggestions. Close a session with **`/close-session`**.
6
+ The handoff is narrative continuity, not a checkpoint, event stream, conversation memory, or audit
7
+ record. Events and archived notes remain inputs to accountability; they do not prove actor identity,
8
+ retention, access control, request binding, or decision reasons by themselves.
6
9
  <!-- rungs:end session -->
@@ -1,6 +1,6 @@
1
1
  [module]
2
2
  name = "session"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  rung = 1
5
5
  summary = "A handoff document carrying state between sessions: resume point, active constraints, and a dated archive."
6
6
 
@@ -61,12 +61,15 @@ paths = [
61
61
  markers = ["rungs:begin session"]
62
62
 
63
63
  [provenance]
64
- sources = ["axiom-mesh"]
65
- patterns = ["session-handoff", "settled-decisions-lock", "dated-session-archive"]
64
+ sources = ["axiom-mesh", "microsoft-agent-framework", "openhands"]
65
+ patterns = ["session-handoff", "settled-decisions-lock", "dated-session-archive", "event-stream-not-audit-log"]
66
66
  incident = """
67
67
  axiom-mesh kept 21 dated handoff notes and a live session document with fixed sections, and its
68
68
  resume state is genuinely readable cold — the only repo of the four where a new session can start
69
69
  without reconstructing context from git. The section that earns its keep is "Active constraints /
70
70
  decisions since last archive", whose wording is a standing instruction not to reopen settled
71
71
  questions: "do not reopen helper ownership, topology, host-boundary … unless explicitly re-planned."
72
+ The framework comparison narrows this handoff to narrative continuity: events and persisted history
73
+ may inform an archive, but they are not an audit record without durability, retention, access control,
74
+ actor identity, request binding, and decision reasons.
72
75
  """
@@ -13,6 +13,10 @@ description: >-
13
13
  The test for everything below: **could a session starting cold tomorrow, with no memory of this
14
14
  conversation, pick up from the document alone?** If not, it is not closed.
15
15
 
16
+ The handoff is narrative continuity, not a checkpoint, conversation memory, event stream, or audit
17
+ record. An archive can point to evidence, but events and persisted history do not establish actor
18
+ identity, retention, access control, request binding, or decision reasons by themselves.
19
+
16
20
  ## 1. Write the archive note
17
21
 
18
22
  `{{archive}}/YYYY-MM-DD_session-NN_<what-closed>-and-<what-is-next>.md`. The filename carries both
@@ -1,4 +1,4 @@
1
- <!-- rungs:begin skills@1.0.0 -->
1
+ <!-- rungs:begin skills@1.1.0 -->
2
2
  ## Skills
3
3
 
4
4
  Multi-step procedures live in [`{{dir}}/`]({{dir}}/) and load only when used. Authoring rules are
@@ -1,6 +1,6 @@
1
1
  [module]
2
2
  name = "skills"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  rung = 2
5
5
  summary = "Skill-authoring discipline and the gates that hold it: spec-pure frontmatter, descriptions that route, neighbours named, and output that lands somewhere."
6
6
 
@@ -78,12 +78,15 @@ an open standard, so the portability objection that justified staying with plain
78
78
  """
79
79
 
80
80
  [provenance]
81
- sources = ["rift-forge", "axiom-mesh", "hexguard"]
82
- patterns = ["invocable-procedure", "skill-neighbours", "prompt-writes-artifact", "operating-skills", "prompt-index-routing"]
81
+ sources = ["rift-forge", "axiom-mesh", "hexguard", "openai-agents-sdk", "microsoft-agent-framework"]
82
+ patterns = ["invocable-procedure", "skill-neighbours", "prompt-writes-artifact", "operating-skills", "prompt-index-routing", "agent-facing-interface", "explicit-output-designation"]
83
83
  incident = """
84
84
  axiom-mesh wrote 21 prompt playbooks, several with no equivalent anywhere else, and gave them no
85
85
  invocation surface at all — they must be found, opened and pasted. hexguard-templates shipped the
86
86
  best decision procedure in the corpus behind a placeholder table. rift-forge, at 13 skills, had to
87
87
  invent the neighbour-naming convention because descriptions alone had stopped disambiguating
88
88
  /curate-mechanic from /curate-champion from /patch-ingest.
89
+ The framework synthesis strengthens that convention: a skill must state who owns continuation, what
90
+ state crosses its boundary, what it may mutate, what bounded output/status it leaves, and where an
91
+ empty or failed result is recorded.
89
92
  """
@@ -59,6 +59,14 @@ A skill that only produces conversation evaporates. **Say in the body where the
59
59
  register row, a document at a known path, a status field. This is what makes a skill set compound
60
60
  instead of being re-derived every session.
61
61
 
62
+ ## Make the agent-facing contract explicit
63
+
64
+ Describe the action shape, the state and observations the agent can see, and the bounded forms of
65
+ empty, truncated, and failed output. Name who owns continuation, what state crosses the skill
66
+ boundary, and what the skill may mutate. Public output is an allow-list of artifacts and statuses;
67
+ internal tool visibility, graph connectivity, and event emission do not make something public by
68
+ accident. If the skill delegates a capability, name the protocol or callback escape hatch.
69
+
62
70
  ## Write standing instructions, not one-time steps
63
71
 
64
72
  The rendered body enters the conversation once and stays for the session; it is not re-read on
@@ -1,4 +1,4 @@
1
- <!-- rungs:begin workflows@1.0.0 -->
1
+ <!-- rungs:begin workflows@1.1.0 -->
2
2
  ## Planning
3
3
 
4
4
  Handed something larger than one change? **`/decompose`** — split into technical concerns, decide
@@ -1,6 +1,6 @@
1
1
  [module]
2
2
  name = "workflows"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  rung = 2
5
5
  summary = "Decision procedures: how to decompose work, whether to reuse or build, and how much planning a piece of work earns."
6
6
 
@@ -54,7 +54,7 @@ paths = ["docs/plans/**/*.md", "docs/**/plan-*.md", "docs/.ai/backlog/plan-*.m
54
54
  markers = ["rungs:begin workflows"]
55
55
 
56
56
  [provenance]
57
- sources = ["hexguard-templates", "hexguard", "rift-forge"]
57
+ sources = ["hexguard-templates", "hexguard", "rift-forge", "swe-agent", "pydantic-ai", "openai-agents-sdk", "microsoft-agent-framework"]
58
58
  patterns = [
59
59
  "reuse-decision-table",
60
60
  "second-consumer-threshold",
@@ -65,6 +65,13 @@ patterns = [
65
65
  "phase-checklist",
66
66
  "lifecycle-verbs",
67
67
  "controlled-performance-comparison",
68
+ "agent-facing-interface",
69
+ "bounded-agent-loop",
70
+ "ownership-changing-handoff",
71
+ "protocol-with-escape-hatch",
72
+ "resumable-approval-state",
73
+ "approval-bound-to-request",
74
+ "explicit-output-designation",
68
75
  ]
69
76
  incident = """
70
77
  hexguard-templates wrote the best decision procedure in the corpus — a nine-step workflow whose
@@ -72,4 +79,8 @@ tier table is introduced with "Use this table instead of 'use judgment'" — and
72
79
  invocation surface at all. Its AGENTS.md prompts table reads, verbatim: "No custom prompts or
73
80
  agents defined yet for this repo. This table is a placeholder for future additions." The procedure
74
81
  loads only when a matching file is edited, which is after the decisions it governs have been made.
82
+ The framework synthesis adds an invocation contract: bounded work must expose its terminal artifact
83
+ and status, handoffs must name continuation ownership and mutation rights, approval must bind to the
84
+ exact pending request, and small protocols must state their escape hatch rather than imply that the
85
+ repository owns runtime transport or authority.
75
86
  """
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: >-
3
+ How to put explicit iteration, cost, time, context, and retry bounds around an agentic invocation,
4
+ and how to name the terminal artifact and status for each bound. Loads when planning work that
5
+ invokes an agent, tool loop, evaluator, or other potentially open-ended procedure.
6
+ paths:
7
+ - "{{plan_path}}/**/*.md"
8
+ - "{{path}}/**/*.md"
9
+ enforcement: review-only
10
+ ---
11
+
12
+ # Bound the invocation
13
+
14
+ “Until done” is not a procedure. Before implementation, declare the limits that actually apply:
15
+ iterations or turns, cost or token budget, wall time, context size, and retries. A bound that is
16
+ only documented in a provider default is not a repository contract.
17
+
18
+ For every limit, name the terminal artifact and status it produces: completed output, partial
19
+ output with a resumable point, a refusal, or an explicit failure. Record what the caller can inspect
20
+ after the bound fires and who owns the next continuation. Empty, truncated, and error results are
21
+ part of the agent-facing interface, not exceptional prose to be invented at the end.
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: >-
3
+ How to specify invocation ownership, state crossings, protocol escape hatches, approval identity,
4
+ and the allow-list of public outputs. Loads when a workflow calls another agent, pauses for a human,
5
+ delegates a capability, or exposes progress to a caller.
6
+ paths:
7
+ - "{{plan_path}}/**/*.md"
8
+ - "{{path}}/**/*.md"
9
+ enforcement: review-only
10
+ ---
11
+
12
+ # Invocation boundaries
13
+
14
+ Treat a composed invocation as a contract, not as a tool-shaped call:
15
+
16
+ - **Ownership:** name who owns continuation, which state crosses the boundary, what the callee may
17
+ mutate, and whether its result returns to the caller.
18
+ - **Protocol:** name the protocol, callback, adapter, or capability check that handles cases the
19
+ local abstraction does not own. A small interface without an escape hatch is a closed assumption.
20
+ - **Approval:** persist a stable pending request id and the exact validated arguments; bind the
21
+ authorized decision server-side, expose an explicit approve/deny response path, and consume it
22
+ once. A caller-supplied boolean is not proof that the surfaced action was approved.
23
+ - **Output:** declare an allow-list of caller-facing artifacts and statuses. Graph connectivity,
24
+ tool visibility, or event emission must not disclose internal progress by accident.
25
+
26
+ These rules describe repository-facing workflow contracts. They do not provide an approval service,
27
+ transport, event stream, sandbox, or authority model.
@@ -5,7 +5,8 @@ description: >-
5
5
  extend upstream / hand-roll), pick the planning tier, and sequence by dependency. Use when handed
6
6
  something larger than a single change, when asked to "plan this", "how should we approach this",
7
7
  "break this down", "scope this out", or before starting work whose shape is not obvious. Also use
8
- across a batch of related work to catch shared concerns once. Executing a tracked item is
8
+ across a batch of related work to catch shared concerns once. For agentic or delegated work, it
9
+ also names the bounds, continuation owner, state crossings, and terminal output/status. Executing a tracked item is
9
10
  /work-item; recording an out-of-scope observation is /record-finding.
10
11
  ---
11
12
 
@@ -60,12 +61,19 @@ out of time for.
60
61
 
61
62
  Following the repo's engineering guidance, not this skill.
62
63
 
64
+ For an agentic invocation, implementation is not complete until the plan or work item records the
65
+ iteration/cost/time/context/retry bounds, the continuation owner, the state and mutation boundary,
66
+ and the terminal artifact/status for completion, truncation, empty output, and failure.
67
+
63
68
  ## 9. Close
64
69
 
65
70
  Reconcile: does the spec still describe what was built? Did any concern's branch change during
66
71
  execution, and is that recorded with its reason? A plan that silently stopped matching reality
67
72
  reads as verified.
68
73
 
74
+ Name the public output allow-list and any protocol escape hatch in the close reconciliation. Internal
75
+ events or connected tools are not public output unless the contract says they are.
76
+
69
77
  ## Two failure modes to watch for
70
78
 
71
79
  - **Resolving the story instead of the concerns.** One search, one answer, four concerns
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@rungs/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Installs and maintains a repository's agentic development system, composed from modules.",
5
5
  "author": "Antoine Dancre",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ThroughTheWind/rungs.git"
9
9
  },
10
- "homepage": "https://github.com/ThroughTheWind/rungs#readme",
10
+ "homepage": "https://docs.rungscli.com",
11
11
  "bugs": {
12
12
  "url": "https://github.com/ThroughTheWind/rungs/issues"
13
13
  },
14
14
  "type": "module",
15
15
  "bin": {
16
- "rungs": "./src/cli.ts"
16
+ "rungs": "dist/cli.js"
17
17
  },
18
18
  "publishConfig": {
19
19
  "access": "public"
@@ -22,12 +22,18 @@
22
22
  "node": ">=22.18"
23
23
  },
24
24
  "scripts": {
25
+ "build": "node scripts/build.mjs",
26
+ "prepack": "npm run build",
27
+ "pretest": "npm run build",
25
28
  "rungs": "node src/cli.ts",
26
- "test": "node --test test/"
29
+ "test": "node --test test/*.test.js"
27
30
  },
28
31
  "files": [
32
+ "dist",
29
33
  "src",
30
- "modules"
34
+ "modules",
35
+ "LICENSE",
36
+ "README.md"
31
37
  ],
32
38
  "keywords": [
33
39
  "agents",
@@ -38,5 +44,8 @@
38
44
  "license": "MIT",
39
45
  "dependencies": {
40
46
  "smol-toml": "^1.8.0"
47
+ },
48
+ "devDependencies": {
49
+ "esbuild": "^0.25.12"
41
50
  }
42
51
  }
package/src/add.ts CHANGED
@@ -262,7 +262,14 @@ export function writeInstallRecord(
262
262
  wroteByModule?: Map<string, Set<string>>,
263
263
  ) {
264
264
  const lines = [
265
- '# Installed by `rungs`. Edit parameters here and re-run `rungs render`.',
265
+ '# Installed by `rungs`. This is a record of what was written, not a control panel:',
266
+ '# editing a parameter here does not rewrite a file that already exists. `rungs render`',
267
+ '# re-emits path-scoped rules from `.ai/rules/`, and `rungs upgrade --apply` replaces',
268
+ '# module files you have not edited — neither re-substitutes parameters. AGENTS.md,',
269
+ '# CLAUDE.md, .gitignore, .gitattributes and .ai/gates.toml are shared between modules,',
270
+ '# so only their `rungs:begin`/`rungs:end` blocks are ever updated; anything outside a',
271
+ '# block, including the entry document\'s title, is yours to edit directly.',
272
+ '#',
266
273
  '# Hashes are what rungs emitted; a file whose hash no longer matches is a',
267
274
  '# divergence rungs reports and never overwrites.',
268
275
  '',
package/src/check.ts CHANGED
@@ -141,7 +141,7 @@ let paramCache: { root: string; params: Params } | null = null;
141
141
  /** Parameters as the repo installed them, falling back to module defaults. */
142
142
  function installedParams(repoRoot: string): Params {
143
143
  if (paramCache?.root === repoRoot) return paramCache.params;
144
- const defaults = resolveParams(loadAllModules(MODULES));
144
+ const defaults = resolveParams(loadAllModules(MODULES), {}, repoRoot);
145
145
  const recordPath = join(repoRoot, '.ai', 'rungs.toml');
146
146
  if (existsSync(recordPath)) {
147
147
  try {
@@ -168,6 +168,7 @@ const tableKey = (engine: string) =>
168
168
  'id-integrity': '__whole__',
169
169
  'render-freshness': 'render_freshness',
170
170
  'register-schema': 'register_schema',
171
+ 'self-declared-closure': 'self_declared_closure',
171
172
  'filename-schema': 'filename_schema',
172
173
  'cross-reference': 'cross_reference',
173
174
  'git-status-reconcile': 'merged_status',