renma 0.18.1 → 0.18.3

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 (84) hide show
  1. package/CHANGELOG.md +126 -1
  2. package/README.md +60 -2
  3. package/architecture.md +4 -0
  4. package/dist/agent-skills.d.ts +5 -6
  5. package/dist/agent-skills.d.ts.map +1 -1
  6. package/dist/agent-skills.js +2 -10
  7. package/dist/agent-skills.js.map +1 -1
  8. package/dist/catalog.d.ts +28 -2
  9. package/dist/catalog.d.ts.map +1 -1
  10. package/dist/catalog.js +65 -16
  11. package/dist/catalog.js.map +1 -1
  12. package/dist/cli-help.d.ts +4 -4
  13. package/dist/cli-help.d.ts.map +1 -1
  14. package/dist/cli-help.js +5 -3
  15. package/dist/cli-help.js.map +1 -1
  16. package/dist/command-invocation.d.ts +4 -0
  17. package/dist/command-invocation.d.ts.map +1 -0
  18. package/dist/command-invocation.js +14 -0
  19. package/dist/command-invocation.js.map +1 -0
  20. package/dist/commands/inspect.d.ts +2 -61
  21. package/dist/commands/inspect.d.ts.map +1 -1
  22. package/dist/commands/inspect.js +51 -149
  23. package/dist/commands/inspect.js.map +1 -1
  24. package/dist/commands/readiness.d.ts +3 -0
  25. package/dist/commands/readiness.d.ts.map +1 -1
  26. package/dist/commands/readiness.js +9 -6
  27. package/dist/commands/readiness.js.map +1 -1
  28. package/dist/commands/suggest-metadata.d.ts +4 -17
  29. package/dist/commands/suggest-metadata.d.ts.map +1 -1
  30. package/dist/commands/suggest-metadata.js +214 -219
  31. package/dist/commands/suggest-metadata.js.map +1 -1
  32. package/dist/decisions/metadata-suggestion.d.ts +60 -0
  33. package/dist/decisions/metadata-suggestion.d.ts.map +1 -0
  34. package/dist/decisions/metadata-suggestion.js +176 -0
  35. package/dist/decisions/metadata-suggestion.js.map +1 -0
  36. package/dist/discovery.d.ts +33 -1
  37. package/dist/discovery.d.ts.map +1 -1
  38. package/dist/discovery.js +365 -51
  39. package/dist/discovery.js.map +1 -1
  40. package/dist/evidence/classification.d.ts +4 -0
  41. package/dist/evidence/classification.d.ts.map +1 -0
  42. package/dist/evidence/classification.js +15 -0
  43. package/dist/evidence/classification.js.map +1 -0
  44. package/dist/evidence/inspect.d.ts +68 -0
  45. package/dist/evidence/inspect.d.ts.map +1 -0
  46. package/dist/evidence/inspect.js +2 -0
  47. package/dist/evidence/inspect.js.map +1 -0
  48. package/dist/evidence/target.d.ts +51 -0
  49. package/dist/evidence/target.d.ts.map +1 -0
  50. package/dist/evidence/target.js +161 -0
  51. package/dist/evidence/target.js.map +1 -0
  52. package/dist/model.d.ts +2 -10
  53. package/dist/model.d.ts.map +1 -1
  54. package/dist/model.js.map +1 -1
  55. package/dist/renderers/inspect.d.ts +3 -0
  56. package/dist/renderers/inspect.d.ts.map +1 -0
  57. package/dist/renderers/inspect.js +159 -0
  58. package/dist/renderers/inspect.js.map +1 -0
  59. package/dist/renderers/metadata-suggestion.d.ts +3 -0
  60. package/dist/renderers/metadata-suggestion.d.ts.map +1 -0
  61. package/dist/renderers/metadata-suggestion.js +320 -0
  62. package/dist/renderers/metadata-suggestion.js.map +1 -0
  63. package/dist/repository-evidence.d.ts +7 -1
  64. package/dist/repository-evidence.d.ts.map +1 -1
  65. package/dist/repository-evidence.js +10 -2
  66. package/dist/repository-evidence.js.map +1 -1
  67. package/dist/scanner.d.ts.map +1 -1
  68. package/dist/scanner.js +58 -4
  69. package/dist/scanner.js.map +1 -1
  70. package/dist/skill-migration.d.ts.map +1 -1
  71. package/dist/skill-migration.js +6 -1
  72. package/dist/skill-migration.js.map +1 -1
  73. package/dist/types.d.ts +72 -0
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js +50 -1
  76. package/dist/types.js.map +1 -1
  77. package/docs/README.md +2 -0
  78. package/docs/agent-skills-compatibility.md +8 -1
  79. package/docs/authoring-guide.md +62 -0
  80. package/docs/diagnostics.md +382 -2
  81. package/docs/internal-architecture.md +296 -0
  82. package/docs/user-manual.md +122 -12
  83. package/package.json +1 -1
  84. package/scripts/verify-package.mjs +154 -9
@@ -0,0 +1,296 @@
1
+ # Renma Internal Architecture
2
+
3
+ This document describes the implemented internal architecture for the 0.18.3
4
+ maintainability release. It is contributor guidance, not a new CLI or JSON
5
+ contract. Renma 0.18.2 remains the behavioral baseline: internal types and
6
+ module boundaries may become clearer, but public fields, classifications,
7
+ diagnostics, severities, exit behavior, and migration direction must not change
8
+ as a side effect.
9
+
10
+ The high-level product boundary remains in [Architecture](../architecture.md).
11
+ Stable classification and decision fields are documented in the
12
+ [Diagnostics Reference](diagnostics.md), and the versioned BOM contract is in
13
+ [Repository Context BOM](repository-context-bom.md).
14
+
15
+ ## Dependency Flow
16
+
17
+ ```text
18
+ repository files + config
19
+ |
20
+ v
21
+ discovery + parsing + repository resolution
22
+ |
23
+ v
24
+ RepositorySnapshot (facts and snapshot-scoped indexes)
25
+ |
26
+ +--> scan + graph --> Readiness --> BOM
27
+ |
28
+ v
29
+ target evidence --> governance evidence --> command decision --> renderer
30
+ ```
31
+
32
+ Dependencies should continue to point from low-level parsing and resolution to
33
+ evidence, then to decisions, renderers, and command orchestration. A lower-level
34
+ module must not import a command renderer. Commands coordinate these layers;
35
+ they should not independently reinterpret repository layout.
36
+
37
+ The dependency direction is checked in CI. Type-only imports are treated as
38
+ architectural dependencies, so lower layers must not import command or renderer
39
+ modules even when the import is erased at runtime.
40
+
41
+ ## RepositorySnapshot Is the Repository Evidence Source
42
+
43
+ `collectRepositorySnapshot` in `src/repository-evidence.ts` performs one
44
+ repository collection and creates the shared in-memory source of repository
45
+ facts. The snapshot contains:
46
+
47
+ - resolved root, configuration, configuration path, and scan count;
48
+ - discovered artifacts and parsed documents;
49
+ - the normalized catalog, Context Lens summary, and diagnostic partitions;
50
+ - repository-relative paths and their captured filesystem states;
51
+ - structural classification evidence indexed by repository-relative path;
52
+ - a parent-Skill index used for exact parent resolution;
53
+ - effective security-policy evidence with its provenance.
54
+
55
+ The indexes belong to the snapshot. Consumers should reuse them instead of
56
+ reparsing files or rebuilding command-specific lookup tables. A new projection
57
+ should normally accept `RepositorySnapshot`, or the narrowest existing
58
+ projection of it, rather than perform another discovery pass.
59
+
60
+ `RepositorySnapshot` does not freeze the working tree. It records what one
61
+ collection read, after which downstream projections operate on that collected
62
+ state. In particular, Readiness builds its graph and scan from one snapshot,
63
+ and BOM builds its graph, scan, Readiness evidence, policy inventory, and
64
+ diagnostics from one snapshot. This prevents one command result from combining
65
+ independently recollected repository states.
66
+
67
+ `collectRepositoryEvidence` is a narrower compatibility projection for
68
+ consumers such as catalog and graph. It deliberately omits parsed documents,
69
+ sets, maps, and indexes that are implementation details.
70
+
71
+ ## Structural And Repository-Backed Resolution
72
+
73
+ Resolution stages answer different questions and must remain separate.
74
+
75
+ ### Repository boundary
76
+
77
+ `repositoryClassificationPath` determines whether a target can be expressed
78
+ relative to one safe repository root. Evidence is considered in this order:
79
+
80
+ 1. an explicit repository root;
81
+ 2. the nearest valid `.git`, `renma.config.json`, or `.renma.json` marker;
82
+ 3. an unambiguous strong structural boundary such as `skills`, `.agents`,
83
+ `contexts`, `context`, `lenses`, or `tools`, plus recognized root files such
84
+ as `AGENTS.md`;
85
+ 4. an unresolved or ambiguous result.
86
+
87
+ Current-working-directory containment is not proof of a repository boundary.
88
+ Renma may be invoked from a parent workspace, and a target may be absolute or
89
+ belong to a neighboring repository. Traversal outside an explicit root is
90
+ rejected instead of being normalized into that root.
91
+
92
+ The names `profiles`, `references`, `examples`, `scripts`, and `assets` are
93
+ negative guard evidence only. They can make a later structural interpretation
94
+ ambiguous, but they never establish a repository root by themselves. Once an
95
+ outer strong boundary has a recognized interpretation, nested boundary-like
96
+ names do not replace it.
97
+
98
+ ### Structural classification
99
+
100
+ `classifyAssetPath` consumes only a normalized repository-relative path and,
101
+ where relevant, parsed metadata type. It produces path interpretation such as
102
+ `kind`, `scope`, `matchedRule`, `reasonCode`, `recognizedRoot`, competing rules,
103
+ ignored nested segments, and a possible parent-Skill path.
104
+
105
+ For Skill-local support, structural placement yields
106
+ `parentResolution: "structural-candidate"`. This means only that the path has a
107
+ canonical Skill-local shape. It does not prove that the candidate file exists,
108
+ is unique, owns the support file, or supplies policy.
109
+
110
+ ### Repository-backed enrichment
111
+
112
+ `buildSkillParentIndex` records all discovered Skill entrypoint candidates by
113
+ logical Skill directory. `resolveSkillSupportParent` then returns `resolved`
114
+ only when exactly one candidate exists; zero candidates are `missing`, and
115
+ multiple candidates are `ambiguous`. `withResolvedSkillParent` attaches that
116
+ result without changing the original structural kind or scope.
117
+
118
+ Snapshot construction creates this parent index before catalog construction and
119
+ passes the same instance into `buildCatalog`. Catalog ownership, target parent
120
+ resolution, and governance enrichment therefore share one snapshot-scoped
121
+ source rather than reconstructing equivalent indexes independently. The public
122
+ `buildCatalog(documents, repositoryPaths)` call remains compatible and creates
123
+ an index for standalone callers.
124
+
125
+ Ownership, policy, catalog membership, and relationships also require
126
+ repository evidence. None of them may be manufactured from path shape alone.
127
+
128
+ ## Shared Target Evidence
129
+
130
+ `inspect` and `suggest-metadata` share the target-evidence pipeline in
131
+ `src/evidence/target.ts`:
132
+
133
+ - `collectTargetDocumentEvidence` reads one target, resolves its repository
134
+ boundary, parses it, and builds structural classification evidence.
135
+ - `collectTargetRepositoryEvidence` collects the resolved repository snapshot
136
+ and enriches the target with catalog membership, parent resolution, policy,
137
+ and governance evidence.
138
+
139
+ The two stages remain separate because a readable file does not guarantee a
140
+ resolvable repository. When repository collection is unavailable, the target
141
+ retains structural evidence but does not fall back to guessed catalog identity,
142
+ parentage, ownership, or policy.
143
+
144
+ Unavailable evidence preserves the exact boundary result:
145
+ `repository-boundary-unresolved` and `repository-boundary-ambiguous` remain
146
+ distinct, while a failure after a root resolves is `snapshot-unavailable`.
147
+
148
+ ## Classification, Governance, And Decisions
149
+
150
+ These evidence layers are related, but none is a substitute for another.
151
+
152
+ | Layer | Answers | Does not prove |
153
+ | --- | --- | --- |
154
+ | Classification | What repository path rule matched, what kind and scope result, and whether a structural or resolved parent is known | Ownership, policy, authority, or human intent |
155
+ | Governance | Whether ownership and policy are declared, inherited, or missing, including provenance | That a proposed edit is applicable |
156
+ | Decision | Whether a command result is deterministic, requires confirmation, is blocked, or recommends no change | A different path classification or permission to discard evidence |
157
+
158
+ Ownership is explicit governance evidence. A declared local owner remains
159
+ declared. A Skill-local file may inherit an effective owner only from exactly
160
+ one resolved parent Skill that declares an owner. Missing or ambiguous parents
161
+ remain unowned. Renma never derives ownership from directory names, prose, Git
162
+ authors, or modification history.
163
+
164
+ Policy provenance is tracked separately from ownership provenance. A local
165
+ policy must not be relabeled as inherited, and an absent effective policy stays
166
+ missing. Likewise, Renma does not infer that local support should be promoted,
167
+ that an independent asset should be created, or that a maintainer intended an
168
+ owner or lifecycle value. Those are human repository-design decisions.
169
+
170
+ ## Decisions, Renderers, And Commands
171
+
172
+ Decision construction belongs in `src/decisions/`. A decision object carries
173
+ the authoritative `decisionStatus`, stable reason code, summary, and any
174
+ remaining human question. Candidate construction must honor that status before
175
+ anything is presented as an applicable edit.
176
+
177
+ Metadata suggestion uses pure builders in
178
+ `src/decisions/metadata-suggestion.ts` for Skill migrations, Skill-local parent
179
+ and governance states, unsupported targets, owner conflicts, and independent
180
+ metadata candidates. The command retains filesystem collision checks,
181
+ candidate assembly, next-action construction, and orchestration.
182
+
183
+ Renderers in `src/renderers/` turn an already-decided result into human text.
184
+ They may improve wording and layout, but they must not rediscover a parent,
185
+ infer governance, change applicability, or create candidate data. JSON output
186
+ serializes the command result directly; it is not reconstructed from rendered
187
+ text.
188
+
189
+ Command modules should stay orchestration-oriented:
190
+
191
+ ```text
192
+ collect context -> build evidence -> decide -> render or serialize
193
+ ```
194
+
195
+ A `no-change-recommended` decision is a successful result. It means Renma
196
+ completed the analysis and found no supported change. The command must not
197
+ manufacture metadata, a migration, or verification work merely to return an
198
+ edit-shaped response.
199
+
200
+ ## Public JSON And Internal Working Types
201
+
202
+ Public machine-readable output is protected at the serialized command boundary.
203
+ Fields such as classification evidence, governance provenance,
204
+ `decisionStatus`, command invocation `command` and `args`, and established
205
+ diagnostic fields must remain compatible with 0.18.2.
206
+
207
+ Internal working types have a different role. `RepositorySnapshot`, parsed
208
+ documents, `ReadonlyMap` indexes, `ReadonlySet` repository paths, and
209
+ intermediate target-resolution unions exist to keep implementation states
210
+ explicit. They are not public JSON merely because they are TypeScript types.
211
+ They may be narrowed or reorganized when behavior-focused tests prove that the
212
+ serialized contract is unchanged.
213
+
214
+ Inspect renderer DTOs live in `src/evidence/inspect.ts`, so renderers do not
215
+ depend on command modules. `src/commands/inspect.ts` re-exports the established
216
+ types to preserve existing TypeScript deep imports.
217
+
218
+ Human-readable reasons and prompts may evolve unless a test intentionally
219
+ protects exact wording. Stable branching must use typed fields such as
220
+ `matchedRule`, `reasonCode`, `parentResolution`, ownership provenance, and
221
+ `decisionStatus`, not prose parsing.
222
+
223
+ ## Intentional 0.18.2 Compatibility Seams
224
+
225
+ Two parallel-looking paths remain intentional in 0.18.3.
226
+
227
+ ### Scan keeps structural parent evidence
228
+
229
+ The snapshot classification index used to annotate scan findings and
230
+ diagnostics is structural. A Skill-local scan detail may therefore retain
231
+ `parentResolution: "structural-candidate"`. Target-oriented commands such as
232
+ `inspect` and `suggest-metadata` enrich the same structural classification with
233
+ snapshot-backed `resolved`, `missing`, or `ambiguous` parent evidence.
234
+
235
+ Do not silently make scan annotations repository-enriched merely to make the
236
+ implementations look uniform. The distinction preserves the 0.18.2 public
237
+ diagnostic shape; changing it requires an explicit contract decision and
238
+ characterization tests.
239
+
240
+ ### Blocked migrations retain partial diagnostic maps
241
+
242
+ A blocked Agent Skills migration may retain partial
243
+ `candidateAgentSkillsFields` and `candidateRenmaMetadata` maps. These fields are
244
+ diagnostic evidence retained for 0.18.2 JSON compatibility, not an applicable
245
+ patch.
246
+
247
+ `decisionStatus` is authoritative. A frontmatter migration is applicable only
248
+ when the decision is not blocked and `canonicalFrontmatter` is present.
249
+ Blocked results must not expose canonical frontmatter or renderer patch
250
+ instructions, and consumers must never treat the partial candidate maps as an
251
+ override of the gate.
252
+
253
+ ## Fail-Closed Boundaries
254
+
255
+ The following constraints are safety and compatibility invariants:
256
+
257
+ - An unresolved or ambiguous repository boundary produces no guessed root,
258
+ catalog identity, inherited governance, or executable repository action.
259
+ - Guard directory names are never positive repository-root evidence.
260
+ - Repository discovery does not follow symbolic links, and a symbolic-link
261
+ marker does not establish a boundary.
262
+ - Structural Skill-local placement never establishes inheritance by itself.
263
+ - A missing or ambiguous parent supplies neither inherited ownership nor
264
+ inherited policy.
265
+ - Explicit local governance is preserved as local governance.
266
+ - A blocked decision suppresses applicable candidate metadata and canonical
267
+ frontmatter; renderers cannot reopen the decision.
268
+ - A no-change decision produces no synthetic work.
269
+
270
+ When one of these states looks inconvenient, preserve it and improve the
271
+ evidence presented to the maintainer. Do not replace uncertainty with a guess.
272
+
273
+ ## Runtime Boundary
274
+
275
+ Renma analyzes declared repository state. It does not execute Skills, select a
276
+ Skill for a live task, assemble prompts, invoke Skill tools, observe model runs,
277
+ or collect runtime telemetry. Static instructions and policies are evidence
278
+ about repository content; they are not proof of runtime behavior.
279
+
280
+ Future runtime signals may be imported as a separate, explicitly versioned
281
+ evidence artifact linked to a repository snapshot or BOM. Signal production,
282
+ collection, storage, and Skill execution remain outside this architecture.
283
+
284
+ ## Contributor Checklist
285
+
286
+ For an internal change:
287
+
288
+ 1. Add or confirm a behavior-focused test for the 0.18.2 result.
289
+ 2. Reuse the snapshot and shared resolution paths before adding another
290
+ collector or index.
291
+ 3. Keep structural facts, repository-backed governance, decisions, and
292
+ rendering in their respective layers.
293
+ 4. Verify stable JSON fields and fail-closed states, not only human text.
294
+ 5. For Readiness or BOM changes, prove all derived sections use one snapshot.
295
+ 6. Run targeted tests, type checking, linting, the full test suite, build, and
296
+ package verification before release.
@@ -73,17 +73,20 @@ support paths:
73
73
 
74
74
  The same reserved names apply under `.agents/skills/**`.
75
75
 
76
- These Skill-local support directories are valid. Keep material local when it is
77
- specific to one Skill. Promote reusable source-of-truth knowledge to an owned
78
- Context Asset, or a helper shared across workflows to `tools/**`, only when
79
- repository evidence supports that change; Renma does not move files
76
+ These valid support paths are structurally Skill-local. Keep material local when
77
+ it is specific to one Skill. Promote reusable source-of-truth knowledge to an
78
+ owned Context Asset, or a helper shared across workflows to `tools/**`, only
79
+ when repository evidence supports that change; Renma does not move files
80
80
  automatically.
81
81
 
82
- Skill-local support belongs only to the nearest Skill directory. When a local
83
- support artifact does not declare an owner, ownership, Readiness, graph, Trust
84
- Graph, and BOM reporting use the nearest Skill's owner as deterministic
85
- effective ownership and mark it as inherited. This does not invent ownership
86
- for shared Context Assets or unrelated repository files.
82
+ Renma claims a parent only after repository evidence resolves exactly one Skill
83
+ entrypoint. When that resolved local support artifact does not declare an owner,
84
+ ownership, Readiness, graph, Trust Graph, and BOM reporting may use the parent
85
+ Skill's owner as deterministic effective ownership and mark it as inherited.
86
+ Missing or ambiguous parents never inherit. See
87
+ [classification evidence](diagnostics.md#how-to-read-classification-evidence)
88
+ for the detailed contract. This does not invent ownership for shared Context
89
+ Assets or unrelated repository files.
87
90
 
88
91
  Only files marked Markdown-parser eligible contribute frontmatter metadata,
89
92
  headings, links, code fences, and repeated-context evidence. Text scripts and
@@ -162,6 +165,61 @@ Review the Skill with this platform's standard Skill authoring guidance, then us
162
165
  Start by running `renma --help` and use command-specific help to choose the appropriate workflow. Make only evidence-backed changes. Do not invent owners, references, product rules, or source-of-truth claims. Preserve existing semantics unless a diagnostic or explicit requirement supports a change. Run `renma scan . --fail-on high` after editing, fix relevant diagnostics, rerun it, and summarize both resolved and remaining findings.
163
166
  ```
164
167
 
168
+ Recommended evidence-first preflight:
169
+
170
+ 1. Run `renma scan . --fail-on high --format json`.
171
+ 2. Run `renma inspect <SKILL.md> --format json`.
172
+ 3. Inspect relevant local resources and referenced Context Assets.
173
+ 4. Use `renma suggest-metadata` only when retrofit or migration evidence exists.
174
+ 5. Prepare the smallest intended patch.
175
+ 6. Rerun `renma scan . --fail-on high --format json`.
176
+ 7. Stop without manufacturing work when `suggest-metadata` reports
177
+ `decisionStatus: "no-change-recommended"`; this currently uses
178
+ `suggestedMode: "no-proposal"`.
179
+ 8. Report unresolved human decisions.
180
+
181
+ For one classification question, `renma inspect <target> --format json` may be
182
+ the initial preflight. Repository-wide work should normally start with `scan`.
183
+
184
+ ## Context Asset Discovery Boundary
185
+
186
+ `contexts/**` is the preferred independent Context Asset root and `context/**`
187
+ remains supported. Nested directory names never override a recognized root.
188
+ Files under canonical Skill `references/`, `profiles/`, `examples/`, `scripts/`,
189
+ and `assets/` directories are structurally Skill-local. Renma claims one parent
190
+ and possible inherited governance only after repository evidence resolves
191
+ exactly one Skill under `skills/**` or `.agents/skills/**`. Supported explicit
192
+ local metadata remains valid but is not required. See
193
+ [classification evidence](diagnostics.md#how-to-read-classification-evidence)
194
+ for the detailed contract.
195
+
196
+ Top-level `references/**` is not a Context root. `tools/**` contains shared
197
+ repository implementation, not Context knowledge, and `skills/**/tools/**` is
198
+ not a canonical local support directory. Skill-local executable support belongs
199
+ under `scripts/`.
200
+
201
+ ```text
202
+ contexts/foo/references/policy.md
203
+ -> independent Context Asset
204
+
205
+ skills/foo/references/policy.md
206
+ -> Skill-local Reference
207
+
208
+ references/policy.md
209
+ -> outside the Context root
210
+
211
+ tools/helper.mjs
212
+ -> repository implementation
213
+
214
+ skills/foo/tools/helper.mjs
215
+ -> not canonical Skill-local support
216
+ ```
217
+
218
+ Placement as independent Context is a human decision about ownership,
219
+ lifecycle, reuse, and source of truth. Classification after placement is
220
+ deterministic. Renma never moves a file based on content. A successful
221
+ `no-proposal` result means no edit should be manufactured.
222
+
165
223
  ## User Story: Create A New Skill With Scaffold
166
224
 
167
225
  Use this flow when adding a new agent-facing Skill. Platform-native guidance
@@ -640,7 +698,19 @@ renma inspect contexts/testing/boundary-value-analysis.md --format json
640
698
  renma inspect skills/testing/spec-review/SKILL.md --lines L10-L42
641
699
  ```
642
700
 
643
- Use this when editing one skill or context file and you want a deterministic outline without reading the whole repository catalog. Without `--lines`, output includes file size, line count, frontmatter range, headings, code fences, links, asset relationships, and a concise Context Lens governance summary when repository context can be inferred. Use `--lines <range>` for an exact source slice; ranges can look like `L10-L42` or `10-42`.
701
+ Use this when editing one skill or context file and you want a deterministic outline without reading the whole repository catalog. Without `--lines`, output includes file size, line count, frontmatter range, headings, code fences, links, asset relationships, and a concise Context Lens governance summary when repository context can be inferred. It also always includes `classification` with `kind`, `scope`, `matchedRule`, `reasonCode`, root or parent evidence, and concise competing rules. A Skill-local path exposes `parentAssetCandidatePath`; `parentAssetPath` appears only when `parentResolution` is `resolved`, while `missing` and `ambiguous` remain explicit fail-closed states. `governance` is separate and reports ownership, policy, and metadata provenance only from repository evidence. Unknown files and repository tools still receive classification. Use `--lines <range>` for an exact source slice; ranges can look like `L10-L42` or `10-42`. `--lines` output itself is unchanged.
702
+
703
+ JSON also includes `repositoryBoundary`. Resolved results record the root,
704
+ repository-relative path, and resolution source. Unresolved results retain a
705
+ stable reason code and `candidateRoots`; structural ambiguity uses
706
+ `repository-boundary-ambiguous` instead of selecting a broad ancestor.
707
+
708
+ For a file target, Renma resolves a repository root from an explicit caller root,
709
+ the nearest safe `.git` file/directory or Renma config marker, or an unambiguous
710
+ structural boundary, in that order. Current-working-directory containment alone
711
+ does not establish a repository. This keeps nested repositories and absolute
712
+ targets outside the current directory deterministic without scanning an
713
+ unrelated parent tree.
644
714
 
645
715
  ### `readiness`
646
716
 
@@ -763,7 +833,17 @@ workflow, constraints, and completion criteria with platform-native guidance;
763
833
  apply only intended metadata or migration changes; run
764
834
  `renma scan . --fail-on high`; fix relevant diagnostics; and rerun the scan.
765
835
  Context Asset output does not receive this Skill-specific authoring guidance.
766
- JSON retains the existing structured suggestion shape.
836
+ JSON adds `classification`, `decisionStatus`, structured `decision` evidence,
837
+ and safe `nextActions` to the existing suggestion fields. Each action has
838
+ `kind` and `invocation`; execute `invocation.command` with the exact
839
+ `invocation.args` array. `invocation.display` is for people and must not be
840
+ parsed as the machine contract, including on paths with spaces, quotes, or
841
+ Windows separators.
842
+
843
+ When no repository root resolves, `decisionStatus` is `blocked` and Renma does
844
+ not emit an executable `scan .` action. Establish the repository root with an
845
+ explicit root or repository marker before verification; the caller's current
846
+ directory is not assumed to contain the target.
767
847
 
768
848
  For Skill targets using the pre-0.16 Renma Skill format, the 0.16.0 metadata
769
849
  migration path is one-way: recognized governance and security frontmatter
@@ -771,6 +851,8 @@ becomes Agent Skills identity plus `metadata.renma.*`. Separately, `skill.md` an
771
851
  report any required entrypoint rename or move, even when their frontmatter
772
852
  already uses Agent Skills fields. For a canonical Agent Skill, `--owner` may
773
853
  instead propose an owner metadata retrofit; it never causes reverse migration.
854
+ Skill migration `sourcePath` and `targetPath` values are repository-relative,
855
+ including when the user invokes Renma from the parent of a nested repository.
774
856
  The normative behavior is documented in
775
857
  [Agent Skills Compatibility and Migration](agent-skills-compatibility.md).
776
858
 
@@ -785,7 +867,35 @@ invalid evidence, confirm the Skill's intent with platform-native authoring
785
867
  guidance, correct the source evidence, rerun `suggest-metadata`, then validate
786
868
  intended corrections with `renma scan . --fail-on high`.
787
869
 
788
- Owner metadata remains recommended but not required. Without `--owner`, `suggest-metadata` blocks owner as a suggested addition and says not to add one unless the asset already declares an owner or a maintainer provides one. With `--owner <owner>`, the command may include that owner because it was explicitly provided. If an existing asset already declares an owner, `suggest-metadata` preserves it; a different `--owner` value is treated as a human-review ownership change, not an automatic metadata suggestion. Renma does not infer owners from Git history, file paths, prose, or authors.
870
+ Owner metadata remains recommended but not required. Without `--owner`, Renma
871
+ does not invent an owner. With `--owner <owner>`, the command may include that
872
+ owner because it was explicitly provided. If an existing asset already
873
+ declares an owner, `suggest-metadata` preserves it; a different `--owner` value
874
+ is treated as conflicting evidence and blocks a candidate. Renma does not infer
875
+ owners from Git history, file paths, prose, or authors.
876
+ If the explicit owner equals the existing canonical `metadata.renma.owner`, the
877
+ result is `suggestedMode: "no-proposal"` with
878
+ `decisionStatus: "no-change-recommended"`; no candidate metadata, canonical
879
+ frontmatter, or patch instruction is emitted.
880
+
881
+ Ordinary Skill-local support returns `suggestedMode: "no-proposal"` and
882
+ `decisionStatus: "no-change-recommended"` when it can inherit governance from
883
+ one resolved parent Skill. Its `candidateMetadata` is empty. A missing or
884
+ ambiguous parent returns `decisionStatus: "blocked"`, leaves governance
885
+ unowned or unresolved, and provides a scan/layout action instead of claiming
886
+ inheritance. Existing explicit local owner or policy metadata is preserved
887
+ under `skill-local-existing-metadata-preserved`; an explicit `--owner` can still
888
+ request a supported intentional override after the parent resolves. Prompt and
889
+ JSON output both distinguish the observed path fact, deterministic
890
+ interpretation, recommendation, and remaining human decision. Repository tools
891
+ and unknown paths never receive fabricated Context metadata candidates.
892
+
893
+ `decisionStatus` is authoritative: `blocked` never authorizes a patch,
894
+ `no-change-recommended` stops without a patch, `human-confirmation-required`
895
+ identifies what must be confirmed before applying only the candidate fields,
896
+ and `deterministic` permits the reviewed candidate. Consumers should include a
897
+ conservative default for unknown future `suggestedMode` values rather than
898
+ assuming every non-`no-proposal` value is applicable.
789
899
 
790
900
  ### `suggest-semantic-split`
791
901
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "renma",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "Manage human-curated context assets for LLMs and agents in Git.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,25 +1,53 @@
1
- import { mkdtemp, readFile, rm } from "node:fs/promises";
1
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
+ import { pathToFileURL } from "node:url";
5
6
 
6
- const cache = await mkdtemp(path.join(os.tmpdir(), "renma-npm-pack-"));
7
+ const temporaryRoot = await mkdtemp(
8
+ path.join(os.tmpdir(), "renma package verification-"),
9
+ );
10
+ const packDirectory = path.join(temporaryRoot, "packed artifact");
11
+ const consumerDirectory = path.join(temporaryRoot, "clean consumer");
12
+ const cacheDirectory = path.join(temporaryRoot, "npm cache");
7
13
 
8
14
  try {
15
+ await mkdir(packDirectory, { recursive: true });
16
+ await mkdir(consumerDirectory, { recursive: true });
17
+ await mkdir(cacheDirectory, { recursive: true });
9
18
  const packed = spawnSync(
10
19
  "npm",
11
- ["pack", "--dry-run", "--json", "--cache", cache],
20
+ [
21
+ "pack",
22
+ "--json",
23
+ "--cache",
24
+ cacheDirectory,
25
+ "--pack-destination",
26
+ packDirectory,
27
+ ],
12
28
  { cwd: process.cwd(), encoding: "utf8" },
13
29
  );
14
- if (packed.error) throw packed.error;
30
+ if (packed.error) {
31
+ throw new Error(`npm pack failed: ${packed.error.message}`);
32
+ }
15
33
  if (packed.status !== 0) {
16
- throw new Error(packed.stderr.trim() || "npm pack --dry-run failed.");
34
+ throw new Error(
35
+ `npm pack failed: ${packed.stderr.trim() || `exit code ${packed.status}`}`,
36
+ );
17
37
  }
18
38
 
19
- const reports = JSON.parse(packed.stdout);
39
+ let reports;
40
+ try {
41
+ reports = JSON.parse(packed.stdout);
42
+ } catch {
43
+ throw new Error("npm pack failed: npm returned invalid JSON output.");
44
+ }
20
45
  const report = reports[0];
21
46
  if (!report || !Array.isArray(report.files)) {
22
- throw new Error("npm pack --dry-run returned no package file list.");
47
+ throw new Error("npm pack returned no package file list.");
48
+ }
49
+ if (typeof report.filename !== "string" || report.filename.length === 0) {
50
+ throw new Error("npm pack returned no tarball filename.");
23
51
  }
24
52
  const files = new Set(report.files.map((file) => file.path));
25
53
 
@@ -27,6 +55,17 @@ try {
27
55
  "package.json",
28
56
  "README.md",
29
57
  "dist/index.js",
58
+ "dist/types.js",
59
+ "dist/types.d.ts",
60
+ "dist/discovery.js",
61
+ "dist/discovery.d.ts",
62
+ "dist/commands/inspect.js",
63
+ "dist/commands/inspect.d.ts",
64
+ "dist/commands/suggest-metadata.js",
65
+ "dist/commands/suggest-metadata.d.ts",
66
+ "dist/skill-migration.js",
67
+ "dist/skill-migration.d.ts",
68
+ "docs/internal-architecture.md",
30
69
  "docs/trust-graph.md",
31
70
  "docs/schemas/repository-context-bom-v2.schema.json",
32
71
  "docs/schemas/trust-graph-v2.schema.json",
@@ -62,11 +101,117 @@ try {
62
101
  }
63
102
  }
64
103
 
104
+ const tarballPath = path.resolve(packDirectory, report.filename);
105
+ const packageRoot = await installInTemporaryConsumer(
106
+ consumerDirectory,
107
+ tarballPath,
108
+ cacheDirectory,
109
+ );
110
+ await verifyInspectDeclarationCompatibility(packageRoot);
111
+ for (const modulePath of [
112
+ "dist/commands/inspect.js",
113
+ "dist/commands/suggest-metadata.js",
114
+ "dist/discovery.js",
115
+ "dist/skill-migration.js",
116
+ ]) {
117
+ try {
118
+ await import(pathToFileURL(path.join(packageRoot, modulePath)).href);
119
+ } catch (error) {
120
+ throw new Error(
121
+ `Deep import failed for ${modulePath}: ${errorMessage(error)}`,
122
+ { cause: error },
123
+ );
124
+ }
125
+ }
126
+
65
127
  process.stdout.write(
66
- `Verified ${files.size} packaged files and every README-relative target.\n`,
128
+ `Verified ${files.size} packaged files, deep imports, inspect declarations, and every README-relative target.\n`,
67
129
  );
68
130
  } finally {
69
- await rm(cache, { recursive: true, force: true });
131
+ await rm(temporaryRoot, { recursive: true, force: true });
132
+ }
133
+
134
+ async function installInTemporaryConsumer(
135
+ consumerDirectory,
136
+ tarballPath,
137
+ cacheDirectory,
138
+ ) {
139
+ await writeFile(
140
+ path.join(consumerDirectory, "package.json"),
141
+ `${JSON.stringify(
142
+ {
143
+ name: "renma-package-verification",
144
+ private: true,
145
+ type: "module",
146
+ },
147
+ null,
148
+ 2,
149
+ )}\n`,
150
+ );
151
+ const installed = spawnSync(
152
+ "npm",
153
+ [
154
+ "install",
155
+ "--ignore-scripts",
156
+ "--no-audit",
157
+ "--no-fund",
158
+ "--package-lock=false",
159
+ "--cache",
160
+ cacheDirectory,
161
+ tarballPath,
162
+ ],
163
+ { cwd: consumerDirectory, encoding: "utf8" },
164
+ );
165
+ if (installed.error) {
166
+ throw new Error(
167
+ `Temporary consumer installation failed: ${installed.error.message}`,
168
+ );
169
+ }
170
+ if (installed.status !== 0) {
171
+ throw new Error(
172
+ `Temporary consumer installation failed: ${installed.stderr.trim() || `npm exited with code ${installed.status}`}`,
173
+ );
174
+ }
175
+ const packageRoot = path.join(consumerDirectory, "node_modules", "renma");
176
+ try {
177
+ await readFile(path.join(packageRoot, "package.json"), "utf8");
178
+ } catch (error) {
179
+ throw new Error(
180
+ `Temporary consumer installation failed: installed renma package is missing (${errorMessage(error)}).`,
181
+ { cause: error },
182
+ );
183
+ }
184
+ return packageRoot;
185
+ }
186
+
187
+ async function verifyInspectDeclarationCompatibility(packageRoot) {
188
+ const declarations = await readFile(
189
+ path.join(packageRoot, "dist/commands/inspect.d.ts"),
190
+ "utf8",
191
+ );
192
+ for (const typeName of [
193
+ "InspectOutline",
194
+ "InspectAssetSummary",
195
+ "InspectRelationship",
196
+ "InspectRelationshipChain",
197
+ "InspectSlice",
198
+ ]) {
199
+ const declared = new RegExp(
200
+ `export\\s+(?:interface|type)\\s+${typeName}\\b`,
201
+ ).test(declarations);
202
+ const reexported = new RegExp(
203
+ `export\\s+type\\s*\\{[\\s\\S]*?\\b${typeName}\\b[\\s\\S]*?\\}\\s*from`,
204
+ ).test(declarations);
205
+ if (!declared && !reexported) {
206
+ throw new Error(
207
+ `dist/commands/inspect.d.ts no longer exports ${typeName}.`,
208
+ );
209
+ }
210
+ }
211
+ }
212
+
213
+ function errorMessage(error) {
214
+ return error instanceof Error ? error.message : String(error);
70
215
  }
71
216
 
72
217
  function requirePackagedPath(files, target) {