@jhlagado/azm 0.2.12 → 0.2.13

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 (44) hide show
  1. package/dist/src/api-compile.d.ts +7 -1
  2. package/dist/src/api-compile.js +17 -5
  3. package/dist/src/api-register-contracts.js +69 -2
  4. package/dist/src/api-tooling.d.ts +1 -1
  5. package/dist/src/cli/artifact-files.d.ts +1 -0
  6. package/dist/src/cli/artifact-files.js +5 -0
  7. package/dist/src/cli/parse-args.d.ts +6 -1
  8. package/dist/src/cli/parse-args.js +59 -0
  9. package/dist/src/cli/run.js +2 -2
  10. package/dist/src/cli/usage.js +5 -0
  11. package/dist/src/cli/write-artifacts.d.ts +1 -1
  12. package/dist/src/cli/write-artifacts.js +15 -2
  13. package/dist/src/expansion/op-expansion.js +12 -1
  14. package/dist/src/index.d.ts +1 -1
  15. package/dist/src/outputs/types.d.ts +13 -1
  16. package/dist/src/register-contracts/analyze-helpers.d.ts +6 -1
  17. package/dist/src/register-contracts/analyze-helpers.js +67 -0
  18. package/dist/src/register-contracts/analyze.d.ts +8 -1
  19. package/dist/src/register-contracts/analyze.js +353 -16
  20. package/dist/src/register-contracts/interfaceContracts.js +45 -0
  21. package/dist/src/register-contracts/liveness.js +23 -0
  22. package/dist/src/register-contracts/policy.d.ts +2 -0
  23. package/dist/src/register-contracts/policy.js +54 -0
  24. package/dist/src/register-contracts/programModel-boundaries.d.ts +5 -1
  25. package/dist/src/register-contracts/programModel-boundaries.js +20 -5
  26. package/dist/src/register-contracts/programModel-routines.js +37 -6
  27. package/dist/src/register-contracts/ratchet.d.ts +3 -0
  28. package/dist/src/register-contracts/ratchet.js +88 -0
  29. package/dist/src/register-contracts/report.d.ts +8 -1
  30. package/dist/src/register-contracts/report.js +174 -0
  31. package/dist/src/register-contracts/smartCommentParsing.js +22 -0
  32. package/dist/src/register-contracts/summary-boundary.js +9 -2
  33. package/dist/src/register-contracts/tooling.d.ts +2 -1
  34. package/dist/src/register-contracts/tooling.js +2 -0
  35. package/dist/src/register-contracts/types.d.ts +157 -0
  36. package/dist/src/syntax/parse-line.js +3 -0
  37. package/docs/codebase/02-source-loading-and-parsing.md +10 -6
  38. package/docs/codebase/04-ops-and-register-contracts.md +49 -4
  39. package/docs/codebase/05-interfaces-and-output-artifacts.md +56 -6
  40. package/docs/codebase/06-verification-and-maintenance.md +10 -2
  41. package/docs/codebase/appendices/a-directory-file-reference.md +3 -1
  42. package/docs/codebase/appendices/b-compile-flow-reference.md +7 -5
  43. package/docs/codebase/appendices/c-public-surface-reference.md +19 -5
  44. package/package.json +1 -1
@@ -1,5 +1,13 @@
1
1
  import type { Z80Instruction } from '../z80/instruction.js';
2
2
  export type RegisterContractsMode = 'off' | 'audit' | 'warn' | 'error' | 'strict';
3
+ export type RegisterContractsReportFormat = 'text' | 'json';
4
+ export type RegisterContractsInferenceFormat = 'json' | 'markdown';
5
+ export type RegisterContractsPolicyMode = 'off' | 'audit' | 'strict';
6
+ export interface RegisterContractsPolicy {
7
+ strict?: readonly string[];
8
+ audit?: readonly string[];
9
+ off?: readonly string[];
10
+ }
3
11
  /** @deprecated Use RegisterContractsMode. */
4
12
  export type RegisterCareMode = RegisterContractsMode;
5
13
  export type RegisterContractsUnit = 'A' | 'B' | 'C' | 'D' | 'E' | 'H' | 'L' | 'IXH' | 'IXL' | 'IYH' | 'IYL' | 'SPH' | 'SPL' | 'carry' | 'zero' | 'sign' | 'parity' | 'halfCarry';
@@ -28,6 +36,10 @@ export type SmartComment = {
28
36
  kind: 'expectOut';
29
37
  carriers: RegisterContractsUnit[];
30
38
  name?: string;
39
+ } | {
40
+ kind: 'rcIgnoreNext';
41
+ findingKind: RegisterContractsFindingKind;
42
+ reason: string;
31
43
  };
32
44
  export interface LocatedSmartComment {
33
45
  file: string;
@@ -47,6 +59,9 @@ export interface RegisterContractsInstruction {
47
59
  file: string;
48
60
  line: number;
49
61
  column: number;
62
+ sourceUnit?: string;
63
+ sourceRelation?: 'entry' | 'include' | 'import';
64
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
50
65
  labels: string[];
51
66
  constants?: ReadonlyMap<string, number>;
52
67
  }
@@ -60,6 +75,9 @@ export interface RegisterContractsRoutine {
60
75
  constants?: ReadonlyMap<string, number>;
61
76
  span: {
62
77
  file: string;
78
+ sourceUnit?: string;
79
+ sourceRelation?: 'entry' | 'include' | 'import';
80
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
63
81
  start: {
64
82
  line: number;
65
83
  column: number;
@@ -78,6 +96,9 @@ export interface RegisterContractsDirectCall {
78
96
  file: string;
79
97
  line: number;
80
98
  column: number;
99
+ sourceUnit?: string;
100
+ sourceRelation?: 'entry' | 'include' | 'import';
101
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
81
102
  }
82
103
  /** @deprecated Use RegisterContractsDirectCall. */
83
104
  export type RegisterCareDirectCall = RegisterContractsDirectCall;
@@ -143,9 +164,13 @@ export interface RoutineSummary {
143
164
  outputCandidates?: RegisterContractsUnit[];
144
165
  }
145
166
  export interface RegisterContractsOutputCandidate {
167
+ kind?: 'output_candidate';
146
168
  file: string;
147
169
  line: number;
148
170
  column: number;
171
+ sourceUnit?: string;
172
+ sourceRelation?: 'entry' | 'include' | 'import';
173
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
149
174
  routine: string;
150
175
  carriers: RegisterContractsUnit[];
151
176
  autoFixable?: boolean;
@@ -154,35 +179,167 @@ export interface RegisterContractsOutputCandidate {
154
179
  /** @deprecated Use RegisterContractsOutputCandidate. */
155
180
  export type RegisterCareOutputCandidate = RegisterContractsOutputCandidate;
156
181
  export interface RegisterContractsConflict {
182
+ kind?: 'definite_contract_violation' | 'flag_lifetime_risk';
157
183
  file: string;
158
184
  line: number;
159
185
  column: number;
186
+ sourceUnit?: string;
187
+ sourceRelation?: 'entry' | 'include' | 'import';
188
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
189
+ routine?: string;
160
190
  callTarget: string;
161
191
  carriers: RegisterContractsUnit[];
162
192
  message: string;
163
193
  }
164
194
  /** @deprecated Use RegisterContractsConflict. */
165
195
  export type RegisterCareConflict = RegisterContractsConflict;
196
+ export type RegisterContractsFindingKind = 'definite_contract_violation' | 'flag_lifetime_risk' | 'missing_callee_contract' | 'unknown_control_flow' | 'external_interface_unknown' | 'output_candidate';
197
+ interface RegisterContractsFindingBase {
198
+ kind: RegisterContractsFindingKind;
199
+ file: string;
200
+ line: number;
201
+ column: number;
202
+ sourceUnit?: string;
203
+ sourceRelation?: 'entry' | 'include' | 'import';
204
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
205
+ message: string;
206
+ routine?: string;
207
+ carriers?: RegisterContractsUnit[];
208
+ }
209
+ export interface RegisterContractsConflictFinding extends RegisterContractsFindingBase {
210
+ kind: 'definite_contract_violation' | 'flag_lifetime_risk';
211
+ callTarget: string;
212
+ }
213
+ export interface RegisterContractsUnknownBoundaryFinding extends RegisterContractsFindingBase {
214
+ kind: 'missing_callee_contract' | 'external_interface_unknown';
215
+ callTarget: string;
216
+ subject: string;
217
+ }
218
+ export interface RegisterContractsStackFinding extends RegisterContractsFindingBase {
219
+ kind: 'unknown_control_flow';
220
+ routine: string;
221
+ stackBalanced: boolean;
222
+ hasUnknownStackEffect?: boolean;
223
+ }
224
+ export interface RegisterContractsOutputCandidateFinding extends RegisterContractsFindingBase {
225
+ kind: 'output_candidate';
226
+ routine: string;
227
+ autoFixable?: boolean;
228
+ }
229
+ export type RegisterContractsFinding = RegisterContractsConflictFinding | RegisterContractsUnknownBoundaryFinding | RegisterContractsStackFinding | RegisterContractsOutputCandidateFinding;
166
230
  export interface RegisterContractsReportModel {
167
231
  entryFile: string;
168
232
  mode: RegisterContractsMode;
169
233
  profile?: string;
170
234
  summaries: RoutineSummary[];
235
+ findings?: RegisterContractsFinding[];
236
+ suppressedFindings?: RegisterContractsSuppressedFinding[];
171
237
  conflicts: RegisterContractsConflict[];
172
238
  outputCandidates?: RegisterContractsOutputCandidate[];
173
239
  unknownCalls: string[];
240
+ ratchet?: RegisterContractsRatchetResult;
241
+ }
242
+ export interface RegisterContractsJsonLocation {
243
+ file: string;
244
+ line: number;
245
+ column: number;
246
+ sourceUnit?: string;
247
+ sourceRelation?: 'entry' | 'include' | 'import';
248
+ sourceUnitRelation?: 'entry' | 'include' | 'import';
249
+ }
250
+ export interface RegisterContractsJsonRemediation {
251
+ category: 'add_contract' | 'fix_call_or_contract' | 'review_control_flow' | 'review_output_contract';
252
+ hint: string;
253
+ }
254
+ export interface RegisterContractsJsonFinding {
255
+ kind: RegisterContractsFindingKind;
256
+ location: RegisterContractsJsonLocation;
257
+ message: string;
258
+ routine?: string;
259
+ callTarget?: string;
260
+ subject?: string;
261
+ carriers?: RegisterContractsUnit[];
262
+ stackBalanced?: boolean;
263
+ hasUnknownStackEffect?: boolean;
264
+ autoFixable?: boolean;
265
+ remediation: RegisterContractsJsonRemediation;
266
+ }
267
+ export interface RegisterContractsSuppression {
268
+ file: string;
269
+ line: number;
270
+ column: number;
271
+ findingKind: RegisterContractsFindingKind;
272
+ reason: string;
273
+ }
274
+ export interface RegisterContractsSuppressedFinding {
275
+ finding: RegisterContractsFinding;
276
+ suppression: RegisterContractsSuppression;
277
+ }
278
+ export interface RegisterContractsJsonReportModel {
279
+ format: 'azm-register-contracts-report';
280
+ version: 1;
281
+ entryFile: string;
282
+ mode: RegisterContractsMode;
283
+ profile?: string;
284
+ summaries: RoutineSummary[];
285
+ findings: RegisterContractsJsonFinding[];
286
+ suppressedFindings?: Array<{
287
+ finding: RegisterContractsJsonFinding;
288
+ suppression: RegisterContractsSuppression;
289
+ }>;
290
+ unknownCalls: string[];
291
+ ratchet?: RegisterContractsRatchetResult;
292
+ }
293
+ export interface RegisterContractsInferenceRoutine {
294
+ name: string;
295
+ in: RegisterContractsUnit[];
296
+ out: RegisterContractsUnit[];
297
+ clobbers: RegisterContractsUnit[];
298
+ preserves: RegisterContractsUnit[];
299
+ confidence: 'explicit' | 'inferred' | 'draft';
300
+ callerImpact: {
301
+ outputCandidateCount: number;
302
+ outputCandidateCarriers: RegisterContractsUnit[];
303
+ };
304
+ }
305
+ export interface RegisterContractsInferenceModel {
306
+ format: 'azm-register-contracts-inference';
307
+ version: 1;
308
+ routines: RegisterContractsInferenceRoutine[];
309
+ }
310
+ export interface RegisterContractsRatchetEntry {
311
+ identity: string;
312
+ finding: RegisterContractsJsonFinding;
313
+ }
314
+ export interface RegisterContractsRatchetResult {
315
+ baselineFile?: string;
316
+ newFindings: RegisterContractsRatchetEntry[];
317
+ removedFindings: RegisterContractsRatchetEntry[];
318
+ changedFindings: Array<{
319
+ identity: string;
320
+ baseline: RegisterContractsJsonFinding;
321
+ current: RegisterContractsJsonFinding;
322
+ }>;
174
323
  }
175
324
  export interface AnalyzeRegisterContractsOptions {
176
325
  mode: RegisterContractsMode;
326
+ policy?: RegisterContractsPolicy;
177
327
  emitReport: boolean;
328
+ reportFormat?: RegisterContractsReportFormat;
178
329
  emitInterface: boolean;
330
+ emitInference?: boolean;
331
+ inferenceFormat?: RegisterContractsInferenceFormat;
179
332
  emitAnnotations?: boolean;
180
333
  fixRegisterContracts?: boolean;
181
334
  registerContractsProfile?: 'mon3';
182
335
  interfaceContracts?: RoutineContract[];
183
336
  acceptedOutputCandidates?: ReadonlyMap<string, RegisterContractsUnit[]>;
337
+ baselineReport?: RegisterContractsJsonReportModel;
338
+ baselineFile?: string;
339
+ ratchet?: boolean;
184
340
  }
185
341
  export interface RegisterContractsAnnotationFile {
186
342
  readonly path: string;
187
343
  readonly text: string;
188
344
  }
345
+ export {};
@@ -67,6 +67,9 @@ function commentOnlyLine(line) {
67
67
  column: firstNonWhitespaceColumn(line.text),
68
68
  ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
69
69
  ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
70
+ ...(line.sourceUnitRelation !== undefined
71
+ ? { sourceUnitRelation: line.sourceUnitRelation }
72
+ : {}),
70
73
  },
71
74
  },
72
75
  ],
@@ -107,10 +107,13 @@ been identified.
107
107
 
108
108
  `src/source/logical-lines.ts` scans a `SourceFile` into `LogicalLine` objects. A
109
109
  logical line records the source name, line number and original text. Tooling
110
- loads can also attach `sourceUnit` and `sourceRelation`:
110
+ loads can also attach `sourceUnit`, `sourceRelation` and `sourceUnitRelation`:
111
111
 
112
112
  - `sourceUnit` is the owning file for the current tooling unit
113
- - `sourceRelation` is `entry`, `include` or `import`
113
+ - `sourceRelation` says how this physical file entered the current expansion
114
+ step: `entry`, `include` or `import`
115
+ - `sourceUnitRelation` says how the owning source unit entered the load:
116
+ `entry` or `import`
114
117
 
115
118
  This thin structure gives every later diagnostic a stable location and enough
116
119
  provenance for tooling features that need to reason about module ownership.
@@ -176,10 +179,11 @@ line is a label, instruction, directive, layout declaration or comment item.
176
179
  - comments
177
180
 
178
181
  Each item carries a source span where appropriate. Tooling spans now preserve
179
- optional `sourceUnit` and `sourceRelation` fields when the loader attached them.
180
- Assembly uses item kind to decide size and emission. Register contract analysis
181
- uses instruction, label and comment items to build routines. D8 map output uses
182
- spans to connect emitted bytes back to files and lines.
182
+ optional `sourceUnit`, `sourceRelation` and `sourceUnitRelation` fields when
183
+ the loader attached them. Assembly uses item kind to decide size and emission.
184
+ Register contract analysis uses instruction, label and comment items to build
185
+ routines. D8 map output uses spans to connect emitted bytes back to files and
186
+ lines.
183
187
 
184
188
  ## Top-Level Parse Order
185
189
 
@@ -168,7 +168,10 @@ without exposing them outside the module.
168
168
  `src/register-contracts/smartComments.ts` reads AZMDoc comments from the comment maps
169
169
  captured during loading. Comment-block splitting and token parsing live in
170
170
  `smartCommentBlocks.ts` and `smartCommentParsing.ts`. External `.asmi`
171
- contracts are parsed in `interfaceContracts.ts`.
171
+ contracts are parsed in `interfaceContracts.ts`. The same parser also accepts
172
+ inline suppression comments in the form
173
+ `;! rc-ignore-next <finding-kind>: <reason>` for the next finding at that
174
+ location.
172
175
 
173
176
  Contracts can describe:
174
177
 
@@ -184,6 +187,11 @@ routine contract. Source comments attach to routines in the current program.
184
187
  The compact source form can place several clauses on one `;!` line, for
185
188
  example `;! in A; out A; clobbers F`.
186
189
 
190
+ `.asmi` parsing stays strict. Files may contain `extern` or `service rst`
191
+ boundaries, `in`, `out`, `clobbers` and `preserves` clauses, then `end`.
192
+ Comment lines are rejected so interface files stay machine-generated and
193
+ deterministic.
194
+
187
195
  ## Effects, Summaries and Liveness
188
196
 
189
197
  Register contract analysis depends on `src/z80/effects.ts`. Effects describe which registers
@@ -212,10 +220,36 @@ summaries before the final pass. Strict mode uses `stackBalanced` and
212
220
  `hasUnknownStackEffect` to distinguish balanced stack use from a routine whose
213
221
  boundary may leave the stack in an unknown state.
214
222
 
223
+ The analysis now emits typed findings rather than only free-form conflict text.
224
+ The current finding set covers:
225
+
226
+ - `definite_contract_violation`
227
+ - `flag_lifetime_risk`
228
+ - `missing_callee_contract`
229
+ - `external_interface_unknown`
230
+ - `unknown_control_flow`
231
+ - `output_candidate`
232
+
233
+ Policy and suppression handling operate on those finding kinds. Report JSON,
234
+ tooling consumers and baseline ratchets all use the same typed finding model.
235
+
236
+ `policy.ts` resolves per-file register-contract modes from `strict`, `audit`
237
+ and `off` glob lists. Specific patterns win over broad patterns, then stricter
238
+ matches win ties. This lets one compile run audit imported wrappers while
239
+ keeping core files in strict mode or disabling analysis for known external
240
+ shims. When policy disables a file, unresolved call targets in that file no
241
+ longer surface as ordinary unknown-symbol diagnostics.
242
+
215
243
  ## Reports, Interfaces and Tooling
216
244
 
217
- `report.ts` renders human-readable `.regcontracts.txt` reports and `.asmi` interface
218
- metadata. It also renders generated source contracts as one compact `;!` line,
245
+ `report.ts` renders human-readable `.regcontracts.txt` reports, JSON report
246
+ payloads, `.asmi` interface metadata and review-oriented inference exports. The
247
+ text and JSON reports share one model with routine summaries, findings,
248
+ suppressed findings, unknown calls and optional ratchet results. Inference
249
+ exports are lighter-weight routine summaries for review workflows and can be
250
+ rendered as JSON or Markdown.
251
+
252
+ `report.ts` also renders generated source contracts as one compact `;!` line,
219
253
  joining `in`, `out`, `maybe-out` and `clobbers` clauses with semicolons.
220
254
  When a routine may clobber the full flag set, the source form uses `F` as the
221
255
  compact carrier name. `annotate.ts`, `annotations.ts`, `fix.ts` and
@@ -225,7 +259,12 @@ conservative fixes.
225
259
  The CLI can request these behaviours through:
226
260
 
227
261
  - `--reg-report`
262
+ - `--reg-report-format`
263
+ - `--reg-baseline`
264
+ - `--reg-ratchet`
228
265
  - `--reg-interface`
266
+ - `--reg-infer`
267
+ - `--reg-infer-format`
229
268
  - `--contracts`
230
269
  - `--fix`
231
270
  - `--accept-out`
@@ -236,6 +275,11 @@ file, line, column, message, fixability and optional text edits. An editor can
236
275
  show the same register contract information that the CLI reports while using
237
276
  normal editor actions for accepted fixes.
238
277
 
278
+ `analyzeRegisterContractsForTools()` now also returns the structured findings
279
+ list and output candidate list beside candidate diagnostics and code actions, so
280
+ tooling integrations can render the same categorised analysis that the CLI
281
+ writes to report artifacts.
282
+
239
283
  ## Changing Ops or Register Contracts
240
284
 
241
285
  Op changes belong in `src/expansion/`, with tests under
@@ -249,7 +293,8 @@ Register contract changes usually begin in one of these files:
249
293
  `instruction-operands.ts`, `instruction-predicates.ts`
250
294
  - Summary inference: `summary.ts`
251
295
  - Caller liveness: `liveness.ts`
252
- - Output text: `report.ts`
296
+ - Policy selection and ratchets: `policy.ts`, `ratchet.ts`
297
+ - Output text and structured exports: `report.ts`
253
298
  - Source edits: `annotate.ts`, `fix.ts`, `annotations.ts`
254
299
  - Tooling surface: `tooling.ts`
255
300
 
@@ -68,6 +68,14 @@ when artifact writing succeeds.
68
68
  artifact suppression, include paths, source-root, case-style linting, directive
69
69
  aliases and register contract options.
70
70
 
71
+ Register-contract parsing now includes:
72
+
73
+ - report format selection with `--reg-report-format text|json`
74
+ - baseline comparison with `--reg-baseline <report.json>`
75
+ - ratcheting with `--reg-ratchet`
76
+ - inference export with `--reg-infer`
77
+ - inference format selection with `--reg-infer-format json|markdown`
78
+
71
79
  `src/cli/write-artifacts.ts` maps parsed options into
72
80
  `CompileNextFunctionOptions` and calculates the output stem.
73
81
  `src/cli/artifact-files.ts` writes in-memory artifacts to disk. If the user
@@ -76,6 +84,13 @@ path and side artifacts use the same base. If the user supplies only
76
84
  `program.asm`, AZM writes outputs next to the entry source using the source
77
85
  stem.
78
86
 
87
+ Register-contract side artifacts now use suffixes derived from the selected
88
+ format:
89
+
90
+ - `.regcontracts.txt` or `.regcontracts.json` for reports
91
+ - `.asmi` for inferred interfaces
92
+ - `.regcontracts.inference.json` or `.regcontracts.inference.md` for review exports
93
+
79
94
  ## Compile API
80
95
 
81
96
  `src/api-compile.ts` exports:
@@ -103,8 +118,14 @@ Important options include:
103
118
  | `d8mInputs` | Artifact paths recorded in D8 metadata. |
104
119
  | `emitBin`, `emitHex`, `emitD8m`, `emitAsm80` | Artifact selection. |
105
120
  | `registerContracts` | Register contract mode. |
106
- | `emitRegisterReport` | Emit `.regcontracts.txt` artifact. |
121
+ | `registerContractsPolicy` | Per-file strict, audit and off policy. |
122
+ | `emitRegisterReport` | Emit text or JSON report artifact. |
123
+ | `registerContractsReportFormat` | Report format, `text` or `json`. |
124
+ | `registerContractsBaseline` | Baseline JSON report for ratcheting. |
125
+ | `registerContractsRatchet` | Fail when findings increase. |
107
126
  | `emitRegisterInterface` | Emit `.asmi` artifact. |
127
+ | `emitRegisterInference` | Emit inference review artifact. |
128
+ | `registerContractsInferenceFormat` | Inference format, `json` or `markdown`. |
108
129
  | `emitRegisterAnnotations` | Emit source annotation artifact. |
109
130
  | `fixRegisterContracts` | Apply conservative source fixes. |
110
131
  | `acceptRegisterOutputCandidates` | Promote selected output candidates. |
@@ -125,6 +146,11 @@ Diagnostics describe every warning or error observed during loading, analysis,
125
146
  register contract analysis, assembly or artifact creation. Artifacts contain the
126
147
  in-memory outputs requested by options.
127
148
 
149
+ When analysis is enabled, `compile()` can now suppress ordinary unresolved-call
150
+ diagnostics for files whose register-contract policy resolves to `off`. That
151
+ keeps policy-controlled external boundaries in the register-contract report path
152
+ rather than duplicating them as symbol failures.
153
+
128
154
  The older option names `registerCare`, `registerCareProfile` and
129
155
  `registerCareInterfaces` remain as deprecated aliases for package consumers.
130
156
  New callers should use the `registerContracts...` names.
@@ -138,17 +164,29 @@ helpers.
138
164
  `loadProgramNext()` returns a loaded program with source items, source texts and
139
165
  source line comments. `analyzeProgramNext()` runs semantic checks and returns
140
166
  symbols. `analyzeRegisterContractsForTools()` returns register contract
141
- diagnostics and code actions in a form suitable for editors.
167
+ diagnostics, typed findings, output candidates and code actions in a form
168
+ suitable for editors.
142
169
 
143
170
  The tooling loader now recognises both `.include` and `.import`. Both directives
144
171
  flatten source into one parse stream. `.import` also marks parsed spans with a
145
172
  new source ownership unit, while `.include` keeps the surrounding owner's unit.
146
- Editor features can use `item.span.sourceUnit` and `item.span.sourceRelation`
147
- to distinguish module-owned declarations from text included into another unit.
173
+ Editor features can use `item.span.sourceUnit`, `item.span.sourceRelation` and
174
+ `item.span.sourceUnitRelation` together: `sourceRelation` tracks the physical
175
+ file edge that produced the parsed item, while `sourceUnitRelation` tracks
176
+ whether the owning unit entered as the entry source or through `.import`.
148
177
  Diagnostics, symbols and source segments continue to use physical source file
149
178
  paths, so imported files appear as their own files in editor and Debug80 map
150
179
  metadata.
151
180
 
181
+ `analyzeProgramNext()` now also enforces the completed import-visibility pass.
182
+ Visibility checks walk instruction operands, data expressions, equates,
183
+ alignment and bin-range expressions and bare `.ds` size expressions. Imported
184
+ private labels remain visible inside their own source unit, including op
185
+ generated references and text included into that same unit. When a name already
186
+ collides with an equate, enum member or case-insensitive type or enum
187
+ declaration, assembly keeps the duplicate-symbol or duplicate-type diagnostic
188
+ instead of replacing it with a visibility error.
189
+
152
190
  An editor integration usually starts with:
153
191
 
154
192
  ```ts
@@ -179,6 +217,7 @@ The output layer uses structured artifact objects from `src/outputs/types.ts`:
179
217
  - `Asm80Artifact`
180
218
  - `RegisterContractsReportArtifact`
181
219
  - `RegisterContractsInterfaceArtifact`
220
+ - `RegisterContractsInferenceArtifact`
182
221
  - `RegisterContractsAnnotationsArtifact`
183
222
 
184
223
  Each artifact has a `kind` field. Callers can switch on `kind` to find the
@@ -241,11 +280,22 @@ supported.
241
280
 
242
281
  Register contract report, interface and annotation artifacts are created through
243
282
  `runRegisterContracts()` in `src/api-register-contracts.ts` and flow through
244
- the same compile result and CLI write path. The report is human-readable. The `.asmi`
245
- interface is metadata that can be loaded by later compile runs through
283
+ the same compile result and CLI write path. Reports can be human-readable text
284
+ or JSON. The `.asmi` interface is metadata that can be loaded by later compile runs through
246
285
  `--interface`. Annotation artifacts write source files when `--contracts` or
247
286
  `--fix` is used.
248
287
 
288
+ The register-contract report artifact now carries optional structured payloads:
289
+ `format`, `json` and the active `findings` list. JSON reports use the stable
290
+ `azm-register-contracts-report` envelope with summaries, findings, suppressed
291
+ findings, unknown calls and optional ratchet results.
292
+
293
+ Inference artifacts carry a routine-oriented `azm-register-contracts-inference`
294
+ model. Each routine records inferred `in`, `out`, `clobbers` and `preserves`
295
+ carriers, an inference confidence and caller-impact counts for output
296
+ candidates. Markdown rendering is intended for review, while JSON is suitable
297
+ for automation and regression tooling.
298
+
249
299
  ## Public API Compatibility
250
300
 
251
301
  The public API is defined by package exports and exported TypeScript types.
@@ -55,8 +55,9 @@ between modules rather than inside one helper.
55
55
 
56
56
  Source-loading and tooling provenance changes belong here as well. The
57
57
  `stage-11-tooling-api.test.ts` integration suite covers include and import
58
- resolution, recursive load failures and the span ownership metadata that tools
59
- read from parsed items.
58
+ resolution, recursive load failures, span ownership metadata and import
59
+ visibility analysis across direct calls, jump fixups, data expressions, bare
60
+ `.ds` sizes, duplicate-symbol precedence and include-only wrappers.
60
61
 
61
62
  ## CLI, ASM80 and Differential Tests
62
63
 
@@ -65,6 +66,10 @@ failure modes, determinism, case-style linting and register contract switches.
65
66
  Users experience the command-line behaviour through argument parsing,
66
67
  diagnostics, output paths and exit status.
67
68
 
69
+ The register-contract CLI suite now also covers JSON report output, inference
70
+ artifact output, baseline loading and ratchet failures so option parsing and
71
+ artifact suffix rules stay stable.
72
+
68
73
  CLI tests also protect deterministic output. `compareDiagnosticsForCli()` sorts
69
74
  diagnostics by file, line, column, severity, code and message. A CLI test can
70
75
  catch changes that leave the compiler correct but make terminal output unstable.
@@ -153,6 +158,8 @@ Ask what kind of fact the change affects:
153
158
  - Instruction-chain splitting belongs in `source/`, while inline source
154
159
  generation and op template expansion belong in `expansion/`.
155
160
  - Routine contracts and liveness belong in `register-contracts/`.
161
+ - Register-contract policy matching, suppression parsing and ratchets also
162
+ belong in `register-contracts/`.
156
163
  - Artifact shape belongs in `outputs/`.
157
164
  - User commands belong in `cli/`.
158
165
  - Package consumers belong in `api-compile.ts`, `api-tooling.ts` and
@@ -187,6 +194,7 @@ This book should change when:
187
194
  - public package exports change
188
195
  - CLI option groups change
189
196
  - output artifact shapes change
197
+ - register-contract finding kinds, policy rules or report schemas change
190
198
  - a major subsystem gains a new responsibility
191
199
  - tests or guardrails are reorganised
192
200
 
@@ -139,11 +139,13 @@ you need to find the owner of a behaviour quickly.
139
139
  | `carriers.ts` | Normalizes register contract carrier names and expands register pairs. |
140
140
  | `controlFlow.ts` | Successor logic for routine instruction flow. |
141
141
  | `profiles.ts` | Built-in external routine profiles such as MON-3. |
142
- | `report.ts` | Renders `.regcontracts.txt`, `.asmi` and compact source contract blocks. |
142
+ | `report.ts` | Renders text and JSON register-contract reports, inference exports, `.asmi` output and compact source contract blocks. |
143
143
  | `annotate.ts` | Inserts or replaces generated contract blocks near routine labels. |
144
144
  | `annotations.ts` | Builds source annotation artifact data. |
145
145
  | `fix.ts` | Finds and applies conservative expected-output fixes. |
146
146
  | `accept-output.ts` | Parses user-accepted output candidate options. |
147
+ | `policy.ts` | Resolves per-file strict, audit and off register-contract policy matches. |
148
+ | `ratchet.ts` | Compares current JSON findings against a baseline report. |
147
149
  | `tooling.ts` | Editor-friendly register contract diagnostics and code actions. |
148
150
  | `types.ts` | Register contract unit, routine, effect, summary, contract and report types. |
149
151
  | `sourceText.ts` | Source line splitting and joining helpers for text edits. |
@@ -23,7 +23,7 @@ compile(entryFile, options, deps)
23
23
  expand textual .include and tooling .import
24
24
  collect source texts
25
25
  collect source line comments
26
- attach source ownership metadata
26
+ attach source ownership metadata and unit ancestry
27
27
  scan logical lines
28
28
  read directive alias profiles
29
29
  build directive alias policy
@@ -43,7 +43,9 @@ compile(entryFile, options, deps)
43
43
  read AZMDoc and .asmi contracts
44
44
  infer summaries
45
45
  run liveness
46
- build report, interface and annotation artifacts
46
+ apply suppressions and scoped policy
47
+ compare JSON findings against optional baseline
48
+ build report, interface, inference and annotation artifacts
47
49
  assembleProgram()
48
50
  buildAddressState()
49
51
  emitProgramImage()
@@ -85,7 +87,7 @@ analyzeProgramNext(loaded)
85
87
 
86
88
  analyzeRegisterContractsForTools(loaded)
87
89
  run register contract analysis in audit-oriented tooling mode
88
- return candidate diagnostics and code actions
90
+ return findings, candidate diagnostics and code actions
89
91
  ```
90
92
 
91
93
  `analyzeRegisterCareForTools()` remains as a deprecated compatibility export for
@@ -97,8 +99,8 @@ tooling integrations that still use the older name.
97
99
  | ------------------ | -------------------- | ------------------------------------- |
98
100
  | Source loading | entry path | logical lines with ownership metadata, source texts, comments |
99
101
  | Parsing | logical lines | source items |
100
- | Analysis | source items | diagnostics, symbols |
101
- | Register contracts | loaded program | summaries, conflicts, reports |
102
+ | Analysis | source items | diagnostics, symbols, import-visibility checks |
103
+ | Register contracts | loaded program | summaries, findings, reports and inference artifacts |
102
104
  | Assembly | source items | byte map, symbols, source segments with per-item columns |
103
105
  | Outputs | byte map and symbols | artifacts |
104
106
  | CLI | artifacts | files on disk |
@@ -44,6 +44,16 @@ want one import path.
44
44
  Use this path for build tools, Debug80 integration and scripts that need bytes
45
45
  or artifacts.
46
46
 
47
+ Compile consumers should treat these register-contract options as public when
48
+ they use that subsystem:
49
+
50
+ - `registerContractsPolicy`
51
+ - `registerContractsReportFormat`
52
+ - `registerContractsBaseline`
53
+ - `registerContractsRatchet`
54
+ - `emitRegisterInference`
55
+ - `registerContractsInferenceFormat`
56
+
47
57
  ## Tooling Exports
48
58
 
49
59
  `@jhlagado/azm/tooling` exposes:
@@ -62,9 +72,10 @@ Use this path for editors, linters and language tooling.
62
72
  `analyzeRegisterCareForTools` remains as a deprecated compatibility export.
63
73
 
64
74
  Tooling consumers should treat parsed item spans as provenance-bearing data.
65
- When present, `sourceUnit` names the owning source unit and `sourceRelation`
66
- records whether that unit entered the load through `entry`, `include` or
67
- `import`.
75
+ When present, `sourceUnit` names the owning source unit, `sourceRelation`
76
+ records whether the physical file edge was `entry`, `include` or `import`, and
77
+ `sourceUnitRelation` records whether the owning unit entered the load through
78
+ `entry` or `import`.
68
79
 
69
80
  ## CLI Export
70
81
 
@@ -95,12 +106,15 @@ Treat these as public contracts:
95
106
  - `D8mSymbol`
96
107
  - `LoadedProgramNext`
97
108
  - `AnalyzeProgramNextResult`
109
+ - `RegisterContractsFinding`
110
+ - `RegisterContractsFindingKind`
111
+ - `RegisterContractsInferenceModel`
98
112
  - `RegisterContractsCandidateDiagnostic`
99
113
  - `RegisterContractsCodeAction`
100
114
 
101
115
  For tooling consumers, this contract also includes the optional
102
- `SourceSpan.sourceUnit` and `SourceSpan.sourceRelation` fields carried on parsed
103
- items.
116
+ `SourceSpan.sourceUnit`, `SourceSpan.sourceRelation` and
117
+ `SourceSpan.sourceUnitRelation` fields carried on parsed items.
104
118
 
105
119
  When these shapes change, update package tests, TypeScript type tests, README
106
120
  examples, repo-local reference docs and this manual.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhlagado/azm",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "AZM assembler for the Z80 family (Node.js CLI)",
5
5
  "license": "GPL-3.0-only",
6
6
  "engines": {