renma 0.18.2 → 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.
- package/CHANGELOG.md +55 -1
- package/architecture.md +4 -0
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +5 -2
- package/dist/catalog.js.map +1 -1
- package/dist/commands/inspect.d.ts +2 -66
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +45 -295
- package/dist/commands/inspect.js.map +1 -1
- package/dist/commands/readiness.d.ts +3 -0
- package/dist/commands/readiness.d.ts.map +1 -1
- package/dist/commands/readiness.js +9 -6
- package/dist/commands/readiness.js.map +1 -1
- package/dist/commands/suggest-metadata.d.ts +4 -21
- package/dist/commands/suggest-metadata.d.ts.map +1 -1
- package/dist/commands/suggest-metadata.js +65 -514
- package/dist/commands/suggest-metadata.js.map +1 -1
- package/dist/decisions/metadata-suggestion.d.ts +60 -0
- package/dist/decisions/metadata-suggestion.d.ts.map +1 -0
- package/dist/decisions/metadata-suggestion.js +176 -0
- package/dist/decisions/metadata-suggestion.js.map +1 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +4 -0
- package/dist/discovery.js.map +1 -1
- package/dist/evidence/classification.d.ts +4 -0
- package/dist/evidence/classification.d.ts.map +1 -0
- package/dist/evidence/classification.js +15 -0
- package/dist/evidence/classification.js.map +1 -0
- package/dist/evidence/inspect.d.ts +68 -0
- package/dist/evidence/inspect.d.ts.map +1 -0
- package/dist/evidence/inspect.js +2 -0
- package/dist/evidence/inspect.js.map +1 -0
- package/dist/evidence/target.d.ts +51 -0
- package/dist/evidence/target.d.ts.map +1 -0
- package/dist/evidence/target.js +161 -0
- package/dist/evidence/target.js.map +1 -0
- package/dist/renderers/inspect.d.ts +3 -0
- package/dist/renderers/inspect.d.ts.map +1 -0
- package/dist/renderers/inspect.js +159 -0
- package/dist/renderers/inspect.js.map +1 -0
- package/dist/renderers/metadata-suggestion.d.ts +3 -0
- package/dist/renderers/metadata-suggestion.d.ts.map +1 -0
- package/dist/renderers/metadata-suggestion.js +320 -0
- package/dist/renderers/metadata-suggestion.js.map +1 -0
- package/dist/repository-evidence.d.ts +7 -1
- package/dist/repository-evidence.d.ts.map +1 -1
- package/dist/repository-evidence.js +10 -2
- package/dist/repository-evidence.js.map +1 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +3 -13
- package/dist/scanner.js.map +1 -1
- package/docs/README.md +2 -0
- package/docs/internal-architecture.md +296 -0
- package/package.json +1 -1
- 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.
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
[
|
|
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)
|
|
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(
|
|
34
|
+
throw new Error(
|
|
35
|
+
`npm pack failed: ${packed.stderr.trim() || `exit code ${packed.status}`}`,
|
|
36
|
+
);
|
|
17
37
|
}
|
|
18
38
|
|
|
19
|
-
|
|
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
|
|
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(
|
|
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) {
|