knodin 0.13.0 → 0.14.0

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.
@@ -5,7 +5,8 @@ Knodin graph promotions leave completed snapshots named
5
5
  They are promotion snapshots, not a supported restore interface. Malformed and
6
6
  legacy names are inventory-only and are never pruning candidates.
7
7
 
8
- Nothing is deleted by default. Inspect one checkout or bounded portfolio roots:
8
+ One-shot pruning is preview-only unless `--apply` is supplied. Inspect one
9
+ checkout or bounded portfolio roots:
9
10
 
10
11
  ```bash
11
12
  knodin backups list
@@ -25,6 +26,48 @@ trees. It unions explicit roots with valid absolute checkout paths in
25
26
 
26
27
  ## Opt-in ongoing retention
27
28
 
29
+ ### Managed storage and legacy consent
30
+
31
+ New successfully indexed stores automatically receive `.knodin/storage-policy.json`
32
+ unless a healthy, applicable bounded global policy already manages that store.
33
+ In that case no local policy file is materialized; `backups policy status` reports
34
+ the effective policy. The local defaults provide:
35
+ one protected recoverable backup, a 1 GiB allocated-backup budget, and a 512 MiB
36
+ free-space reserve. Post-promotion maintenance runs after releasing the operation
37
+ lease. Count/byte quotas override the age window, but never recovery protection.
38
+ If protected data exceeds the quota, Knodin reports it and refuses unsafe further
39
+ candidate allocation; increase the budget or resolve the storage condition.
40
+
41
+ Existing unmanaged graphs require explicit consent:
42
+
43
+ ```bash
44
+ knodin backups policy status /path/to/repo --json
45
+ knodin backups policy preview /path/to/repo --json
46
+ knodin backups policy apply /path/to/repo --json
47
+ knodin backups policy apply /path/to/repo --max-count 2 --max-bytes 2147483648
48
+ knodin backups policy apply /path/to/repo --mode preview
49
+ knodin backups policy apply /path/to/repo --mode disabled
50
+ ```
51
+
52
+ Preview writes nothing and includes the pruning plan and bounded inventory.
53
+ Apply persists consent and runs policy-approved backup maintenance. Omitted
54
+ settings preserve an existing policy, including explicit dry-run. Use
55
+ `--mode enabled` to explicitly return to deleting eligible managed backups.
56
+ `--reserve-bytes` configures free space retained beyond a conservative scratch
57
+ estimate. Unknown capacity/allocated sizes refuse allocations instead of
58
+ guessing zero. The estimate is not a reservation against other writers.
59
+
60
+ Candidate maintenance removes only managed empty or audit-only directories with
61
+ a validated owner marker and confirmed exited owner. Owner-unknown legacy
62
+ directories, live owners, DB-bearing interrupted candidates, symlinks, leases
63
+ and promotion markers are protected. Permission/deletion failures and protected over-budget data are
64
+ visible, not claimed as reclaimed space. Allocated versus apparent totals differ;
65
+ APFS clones can share extents, so allocated-byte sums are not promised physical
66
+ recovery. Source mirrors and external development harness directories are
67
+ separate storage classes, not promotion backups.
68
+
69
+ ### Native daily scheduler
70
+
28
71
  Installation is the consent boundary:
29
72
 
30
73
  ```bash
@@ -34,7 +77,8 @@ knodin backups retention doctor
34
77
  knodin backups retention remove
35
78
  ```
36
79
 
37
- Options are `--retention-days` (3), `--keep-newest` (1), `--depth` (5),
80
+ Options are `--retention-days` (3), `--keep-newest` (1), `--max-count` (1),
81
+ `--max-bytes` (1073741824), `--depth` (5),
38
82
  `--schedule` (`04:17` local), and `--dry-run`. Installation writes a versioned
39
83
  policy to `$XDG_CONFIG_HOME/knodin/backup-retention.json` and owned receipt/run
40
84
  state beneath `$XDG_STATE_HOME/knodin/backup-retention/`. Ordinary Knodin use
package/docs/CLI.md CHANGED
@@ -37,8 +37,12 @@ knodin update --help
37
37
 
38
38
  Backup discovery and pruning are native and cross-platform. `knodin backups
39
39
  list` and the default `knodin backups prune` are read-only; deletion requires
40
- `--apply`, while recurring cleanup requires the separate `knodin backups
41
- retention install` consent step. See `docs/BACKUP-RETENTION.md` for safety,
40
+ `--apply`. New successfully indexed stores get bounded post-promotion retention
41
+ (one rollback, 1 GiB allocated-backup budget, 512 MiB free-space reserve).
42
+ Existing stores require `knodin backups policy preview` followed by explicit
43
+ `policy apply`; status/preview expose bounded inventory and exact cleanup plans.
44
+ Native daily scheduling remains opt-in through `knodin backups retention install`.
45
+ See `docs/BACKUP-RETENTION.md` for safety,
42
46
  scheduler, logging, and removal details.
43
47
 
44
48
  `repos search` resolves configured system memberships independently for every
@@ -227,8 +231,8 @@ configuration `consumers_of`. `count` is total observed matching sites,
227
231
  `files` contains aggregates only for files represented on this page.
228
232
  `targetKind` identifies the resolved declaration as `class` or `interface` and is
229
233
  preserved on every page. Interface consumers cover supported type-position roles,
230
- not constructors, static-field reads, `implements` relationships, or inferred
231
- runtime dispatch. Query implementation relationships separately.
234
+ not constructors, static-field reads, static-method receivers, `implements`
235
+ relationships, or inferred runtime dispatch. Query implementation relationships separately.
232
236
 
233
237
  Use `--test-scope test|production|all` (default `all`), a canonical repository-relative
234
238
  `--path` prefix, and stable `--identity`/`--file` target selectors when needed.
@@ -240,10 +244,13 @@ A minimum complete site plus qualifications must fit; source fields are never sh
240
244
 
241
245
  Sites retain exact spelling, role, source-owner identity when known, and exclusive
242
246
  end positions: one-based lines/UTF-16 columns and zero-based UTF-8 byte offsets.
243
- The target's own file is excluded. Nine supported syntax roles do not cover every
247
+ The target's own file is excluded. Supported syntax roles include `instanceof`
248
+ types and explicit static-method receivers, but do not cover every
244
249
  Apex use, metadata relationship, or runtime path. Read `classConsumerAnalysis` and
245
250
  `indexCoverage` before interpreting a negative; missing/invalid descriptors and
246
251
  unresolved uses remain qualified, not proof that a class is unused.
252
+ Legacy descriptors without receiver or `instanceof` capability require
253
+ `knodin repair` to re-extract affected files within the declared index scope.
247
254
  An indexed retained interface declaration without a safely matching interface
248
255
  symbol returns `CLASS_CONSUMER_TARGET_CAPABILITY_MISSING`, `targetResolution: capability-missing`,
249
256
  and a nonzero exit—not a successful zero or a fabricated identity. Follow the
@@ -6,6 +6,47 @@ reference. It does not prove that reflection, runtime dependency injection,
6
6
  framework configuration, metadata, deployed infrastructure, or external
7
7
  consumers cannot invoke the code.
8
8
 
9
+ For Apex, `dead_code` can also report connected methods with
10
+ `candidateReason: "unrooted-chain"`: no modeled root reaches that chain in
11
+ the indexed inventory, even though its methods reference one another.
12
+ `deadCodeAnalysis.rootReachability` records the model, counts, limitations,
13
+ stable chain identities, method membership, structural owners and call edges.
14
+ Owner membership is not a finding that the entire class is unused.
15
+
16
+ Completeness has two separate scopes. `deadCodeAnalysis.partial`, labeled
17
+ `partialScope: "apex-platform-declarations"`, reports unsupported or missing
18
+ Apex declaration capabilities. It does not certify complete call resolution.
19
+ `deadCodeAnalysis.rootReachability.partial` independently reports uncertainty
20
+ in the indexed root/call analysis. Complete declaration coverage can therefore
21
+ coexist with partial root reachability; inspect both before interpreting results.
22
+
23
+ `deadCodeAnalysis.platformEntries` separately describes matching indexed
24
+ org-invokable declarations. It does not add them to candidate `results` or
25
+ change the candidate `count`. Returned entries include identity, location and
26
+ source-verified `platformEntry` evidence, or an explicit unavailable reason.
27
+ The classification total is not a verified-proof count or evidence of runtime use.
28
+
29
+ This collection is a bounded preview: at most 25 entries, further limited by
30
+ the query limit, with at most eight source-file verification attempts and a
31
+ 1 MiB bound per file. Each selected file is verified once per request. Counts
32
+ separate verified, unavailable and omitted entries; `truncated`/`partial` and
33
+ omission reasons disclose incomplete delivery. There is no collection cursor:
34
+ narrow the path or use identity-qualified `explain` for a specific declaration.
35
+ Response budgets preserve the returned collection or fail explicitly.
36
+
37
+ An org-invokable declaration may also have static callers, including Flow
38
+ metadata. These tags do not assert no static callers and do not replace
39
+ `callers_of` evidence. `runtimeInvocation: "not-established"` remains explicit.
40
+
41
+ The analysis considers platform declarations, triggers, test declarations and
42
+ supported metadata boundaries before filtering the requested output paths.
43
+ Possible local calls conservatively protect reachable targets; unresolved
44
+ reflection, missing provenance and bounded-inventory omissions remain qualified.
45
+ An omitted candidate is not proof of runtime use. Root reachability is scoped
46
+ to indexed source, with `runtimeInvocation: "not-established"` and
47
+ `safeToDelete: false`. Response budgets preserve the root-analysis contract or
48
+ fail explicitly when it cannot fit; narrow the request or increase its budget.
49
+
9
50
  Impact follows source-evidenced resolved relationships. High-confidence
10
51
  incoming cross-repository relationships can make a dependency visible to
11
52
  impact and dead-code analysis. Heuristic or unresolved relationships lower
package/docs/MCP.md CHANGED
@@ -4,6 +4,14 @@ knodin exposes exactly one MCP tool named `knodin`. Capabilities such as
4
4
  context, explain, review, search, docs, doctor, repositories, and systems are
5
5
  operations of that gateway, not separate top-level tools.
6
6
 
7
+ Indexing and repair use the same storage-capacity and managed-retention guards
8
+ as the CLI. Low or unknown capacity refuses scratch allocation without replacing
9
+ the active graph. Maintenance warnings go to stderr, not the stdio JSON protocol.
10
+ Repository storage-policy consent and inventory are CLI administrative commands:
11
+ `knodin backups policy status|preview|apply /path/to/repo --json`.
12
+ Existing unmanaged stores do not gain destructive cleanup through an MCP read.
13
+ See `docs/BACKUP-RETENTION.md` for limits, migration and recovery protection.
14
+
7
15
  Shared-index support also stays inside that one gateway. Status, context,
8
16
  query, explain, review, map, and search responses include a bounded
9
17
  `sharedIndex` provenance object. `source: "shared-snapshot"` identifies origin;
@@ -229,8 +237,9 @@ and `limit` (1–1000 sites). This is separate from method calls and Java config
229
237
  and `returnedCount` counts delivered sites; page-local `files` aggregates do not
230
238
  enumerate every matching file until paging is complete.
231
239
  The protected `targetKind` is `class` or `interface`. Interface targets admit
232
- supported type-position roles, not constructor/static-field uses. `implements`
233
- relationships and concrete runtime dispatch are not typed consumer sites.
240
+ supported type-position roles, including `instanceof`, not constructor/static-field
241
+ or static-method-receiver uses. `implements` relationships and concrete runtime
242
+ dispatch are not typed consumer sites.
234
243
 
235
244
  Use the returned `continuation` with the same target and filters. Byte/token/item
236
245
  budgets may vary. The signed token is interchangeable with CLI `--continuation`
@@ -239,10 +248,13 @@ qualifications fit before the cursor is signed; no generic truncation follows.
239
248
  An extra limit-one backend read currently revalidates the snapshot before delivery;
240
249
  this correctness guard is not a claim of optimized paging cost.
241
250
 
242
- The nine supported Apex roles exclude target-self-file sites and do not establish
251
+ Supported Apex roles include explicit static-method receivers and `instanceof`
252
+ types, exclude target-self-file sites and do not establish
243
253
  complete class-use or runtime reachability. `classConsumerAnalysis` reports partial
244
254
  extraction and indexed-inventory counters, separately from `indexCoverage`. Unknown
245
255
  targets and ambiguity are unavailable, never successful zero-site answers.
256
+ Legacy descriptors without receiver or `instanceof` capability remain partial;
257
+ follow the scope-preserving `knodin repair` guidance to re-extract them.
246
258
  `CLASS_CONSUMER_TARGET_CAPABILITY_MISSING` with `targetResolution: "capability-missing"`
247
259
  means retained indexed interface evidence exists but its symbol capability is
248
260
  missing. It is unavailable without a fabricated target identity or zero count;
@@ -106,16 +106,34 @@ and [dynamic picklist configuration guide](https://developer.salesforce.com/docs
106
106
 
107
107
  ## Apex class-use sites are a separate query
108
108
 
109
- `query class_consumers` exposes static-field reads, constructors, variable types,
110
- casts, class literals, parameter types, generic parameter types, loop-variable
111
- types, and generic type arguments. It does not widen metadata bindings or method
109
+ For a selected Apex platform entry declaration, `explain` also exposes
110
+ `platformEntry`: the declared contract, reason, source hash and exact declaration
111
+ and owner spans. `runtimeInvocation: "not-established"` is intentional: a
112
+ Batchable, Queueable or other platform-facing declaration does not prove that
113
+ an org invokes it. Missing or mismatched declaration evidence is explicitly
114
+ unavailable, not a positive entry classification. Ordinary helper declarations
115
+ do not gain a platform-entry label merely from global visibility.
116
+
117
+ This evidence is separate from class-use sites and dead-code candidate filtering.
118
+ CLI/MCP source and compact modes retain the qualification; a budget too small
119
+ for the complete platform-entry field fails explicitly rather than dropping it.
120
+
121
+ `query class_consumers` exposes static-field reads, static-method receivers,
122
+ constructors, variable types, casts, `instanceof` types, class literals,
123
+ parameter types, generic parameter types, loop-variable types, and generic type
124
+ arguments. It does not widen metadata bindings or method
112
125
  call edges. Case-insensitive type resolution requires unique declarations and
113
- validated owner chains; value/type shadows, malformed source and ambiguous
114
- catalog entries cannot become confident class matches.
126
+ validated owner chains. Relevant lexical shadows, malformed source and ambiguous
127
+ catalog entries cannot become confident class matches. A value-name shadow does
128
+ not hide an explicit `instanceof` RHS type.
115
129
 
116
130
  Results exclude the target's own source file and include test files by default.
117
131
  Missing descriptors, unresolved uses and unsupported forms qualify the observed
118
- counts. Static method receivers, return types, inheritance and writes are not
119
- promoted into these nine roles. These source-evidenced sites are not complete
120
- Apex usage enumeration or runtime reachability proof. See the CLI/MCP paging
132
+ counts. Method receivers require a unique same-owner method name and arity
133
+ before the static modifier is accepted; same-arity overloads remain ambiguous.
134
+ Return types, inheritance and writes are not promoted into these roles.
135
+ Legacy descriptors without receiver or `instanceof` capability remain partial;
136
+ run `knodin repair` to re-extract affected files within the declared index scope.
137
+ These source-evidenced sites are not complete Apex usage enumeration or runtime
138
+ reachability proof. See the CLI/MCP paging
121
139
  sections for signed continuations and exact site-position conventions.
@@ -0,0 +1,216 @@
1
+ # knodin 0.13.1
2
+
3
+ Compatible patch release for KNODIN-47 and KNODIN-48: correct false stale
4
+ evidence after repair and bound managed graph storage.
5
+
6
+ ## Fixed
7
+
8
+ - Align freshness probes with source-indexing eligibility. Non-source files and
9
+ collapsed untracked directories no longer make an unchanged, repaired graph
10
+ appear stale.
11
+ - Preserve exact Git paths, including renames and names containing whitespace,
12
+ and exclude canonical pruned trees before expanding untracked files.
13
+ - Track relevant `.gitignore` rules, including tracked policy files omitted by
14
+ Git's stat cache, Git's local exclude file, and its
15
+ effective global exclude file with bounded reads. Retain previously indexed
16
+ paths for deletion detection and respect scoped-index path safety.
17
+ - Encode dirty path sets without newline collisions. Failed Git or ignore-policy
18
+ checks remain unknown, including Git checkouts without their first commit.
19
+ - Reindex relevant dirty or committed-diff source before accepting a repaired
20
+ baseline, so restored file sizes and timestamps cannot hide source changes.
21
+ Repair's initial audit also checks change times against the completed source snapshot,
22
+ covering source edits omitted by Git's stat-cache shortcuts.
23
+ Verify source and Git stability before publishing the baseline; unresolved
24
+ historical evidence requires explicit recovery rather than a false success.
25
+ - Preserve first-time repair of an empty graph while refusing to guess missing
26
+ history or refresh timestamps for an existing graph.
27
+ - Preserve completed-file resume behavior in direct-engine repairs with content
28
+ proofs that are invalidated by later index writes. Public repair retains its
29
+ atomic candidate cancellation behavior.
30
+ - Separate repair's content-proof epoch from ordinary reconciliation timestamps.
31
+ Existing non-empty Git graphs need a one-time `knodin index --clean` before
32
+ this versioned proof is established. Status, query freshness, and repair
33
+ refuse to certify missing, malformed, or future-dated provenance rather than
34
+ trusting a legacy timestamp.
35
+ - Force-reindex paths that Git reports changed, even when size and timestamp
36
+ match the indexed tuple, so ordinary reconciliation cannot retain old symbols.
37
+ - Establish Knodin's existing repository-local state exclusion before candidate
38
+ indexing and repair record their policy baseline.
39
+ - Accept the platform null device as an empty global Git exclude policy, and
40
+ retain the documented bounded stat fallback for verified nested Git paths.
41
+ - Bound new manifest eligibility reads together with ignore-policy reads. When
42
+ the freshness budget is exceeded, report unknown; explicitly index a large
43
+ new manifest before relying on automatic freshness for it.
44
+ - Revalidate already-dirty source content with a bounded proof (at most 256
45
+ paths and 1 MiB per probe). Over-budget content or unreliable Git stat settings
46
+ remain unknown on the cheap freshness path. A complete deep audit can verify
47
+ the retained source snapshot; unknown is not evidence that nothing changed.
48
+ - Validate clean-build resume provenance strictly, preserve the original HEAD,
49
+ recheck equal-stat source edits, and remove deleted paths before promotion.
50
+ Source/Git changes during clean indexing or final promotion refuse publication
51
+ of the candidate rather than minting a proof over a mixed snapshot.
52
+ - Preserve verified parsed-source proofs when seeding an unchanged linked
53
+ worktree. Missing, malformed, or mismatched copied proofs require extraction;
54
+ omitted equal-stat source edits cannot retain stale sibling symbols.
55
+ - Reuse scoped, canonically pruned Git enumeration for cold reconciliation,
56
+ including unborn repositories. Failed or over-budget working-tree/commit-diff
57
+ enumeration cannot advance the stored HEAD or freshness baseline. Missing
58
+ shallow/foreign commit history requires explicit clean-index recovery rather
59
+ than a stat-only fallback that misses equal-stat source edits.
60
+ - Withhold a first scoped content-proof epoch after unhealthy verification or
61
+ an exception, including failures after the deep audit completes. Existing
62
+ verified epochs are not deleted by failed scope widening.
63
+ - Preserve structural revisions and Apex consumer paging snapshots across
64
+ unchanged cold opens, including unborn Git repositories. Canonical discovery
65
+ and relationship/semantic maintenance always run; only unchanged file
66
+ extraction is skipped using an exact retained parsed-source proof. Missing,
67
+ malformed, or changed per-file evidence forces extraction. The extraction
68
+ fence also retains proof before a repository epoch exists, using bounded-memory
69
+ streaming reads before and after parsing (additional source I/O, not a measured
70
+ performance gain). Failed extraction cannot publish a new per-file proof;
71
+ aggregate completion still requires healthy verification.
72
+ Worker-queued inputs also retain pre/post-parse source fences; per-queued-file
73
+ metadata scales with the source inventory, while streaming uses a 64 KiB read
74
+ buffer. Successful batched writes retain their parsed proofs; rolled-back
75
+ file writes cannot regain a proof at the batch boundary.
76
+ Stable unparsed/skipped source reads also retain their consumed-source proof
77
+ after pre/post-read verification; extraction and database-write errors do not.
78
+ Batch write failures retain successful per-file work but refuse aggregate
79
+ completion before publishing an epoch or baseline; resolve the write error
80
+ and retry indexing. A failed candidate is not promoted over the active graph.
81
+
82
+ ## Storage stewardship (KNODIN-48)
83
+
84
+ New successfully indexed stores receive a repository-local storage policy:
85
+ one recoverable promotion backup, a 1 GiB allocated-backup budget, and a
86
+ 512 MiB free-space reserve. Count/byte budgets override the age grace period.
87
+ Cleanup runs after durable promotion and operation-lease release; cleanup
88
+ failures are reported without rolling back the committed graph. The newest
89
+ snapshot and an integrity-checked rollback remain protected, even when that
90
+ requires reporting an over-budget condition instead of deleting recovery data.
91
+
92
+ Existing unmanaged stores are not silently migrated. Run
93
+ `knodin backups policy preview /path/to/repo --json`, inspect exact candidates,
94
+ then `knodin backups policy apply /path/to/repo --json` to consent. Existing
95
+ explicit dry-run/disabled policies remain respected. Configure limits with
96
+ `--max-count`, `--max-bytes`, and `--reserve-bytes`; `--mode preview` or
97
+ `--mode disabled` records the corresponding maintenance choice.
98
+
99
+ Automatic cleanup rechecks effective policy under its lease before deletion.
100
+ New-store initialization eligibility is durably recorded in the graph before
101
+ publication and survives later clean replacement if policy publication fails;
102
+ successful initialization clears it, so deliberate policy removal stays unmanaged.
103
+ Clearing this internal marker preserves an already validated graph audit and its
104
+ original timestamp across clean close/reopen; concurrent database changes refuse
105
+ audit carry-forward rather than being labeled as previously verified.
106
+ Policy reads use the shared external-state resolver for registered mirrors;
107
+ policy initialization, apply, and automatic cleanup refuse mirror source writes,
108
+ including source aliases. Unsupported Windows directory syncing does not turn
109
+ an already file-synced policy publication into failure; other I/O errors remain visible.
110
+
111
+ Candidate creation and repair's VACUUM preflight require conservatively
112
+ estimated scratch space plus the reserve. Unknown capacity or allocated-size
113
+ measurements fail closed before allocating scratch. This is an estimate, not
114
+ a disk reservation; ENOSPC and concurrent writers can still cause refusal.
115
+ Bounded candidate sweeping removes only managed empty/audit-only residue with
116
+ a validated marker and confirmed exited owner. Unknown legacy owners and
117
+ DB-bearing resumable candidates are preserved. Inventory distinguishes apparent from
118
+ allocated bytes; APFS shared extents mean allocated totals are not guaranteed
119
+ physical reclamation. External source mirrors and development acceptance
120
+ directories are not graph backups and are not blanket-deleted by this policy.
121
+ Candidate allocation revalidates the managed root and new directory before any
122
+ owner or database write. JSON-manifest eligibility uses a no-follow descriptor
123
+ with final pathname identity checks. Shared-index restore always releases its
124
+ lifecycle lease and invokes post-promotion storage finalization even when cache
125
+ lease release or candidate cleanup fails.
126
+
127
+ The release auto-merge dispatch retains exact-SHA, unresolved-conversation,
128
+ local final-gate, approval, and atomic-merge protections when Copilot review is
129
+ unavailable because its AI-credit budget is exhausted. The explicit fallback
130
+ reason is opt-in; ordinary dispatches still require a clean exact-head review.
131
+ The exhausted-credit path also covers a new PR for which Copilot cannot create
132
+ any review object; an exact-head review containing findings still blocks merge.
133
+ The dispatch runs on the organization's approved self-hosted Linux pool so IP
134
+ allow-list enforcement does not prevent it from reading or merging its own PR.
135
+ The tagged release verifier allows 45 minutes for the same full-suite workload,
136
+ preventing a successful test run from being cancelled by the former 30-minute cap.
137
+
138
+ ## Release verification
139
+
140
+ Release-candidate pushes run full coverage without repeatedly scanning Sonar. After the current
141
+ PR head has completed review, maintainers run `npm run release:verify` locally; it runs full
142
+ coverage and the local Sonar quality gate, refuses a dirty tracked tree or remote-head mismatch,
143
+ and records the exact verified SHA in Git-local metadata. Any later commit invalidates that result.
144
+ The gate also refuses branch changes and ambiguous remote references. Isolated
145
+ subprocess regressions cover preflight refusals, verification failures, SHA drift,
146
+ and the evidence payload without invoking real scans.
147
+
148
+ Ordinary PR pushes no longer enable auto-merge. Maintainers explicitly dispatch
149
+ the auto-merge workflow with the PR number and exact SHA only after independently
150
+ verifying clean current-head review and successful local final-gate evidence.
151
+ The dispatch checks the live open PR head; it does not authenticate local evidence
152
+ or replace protected-branch checks.
153
+
154
+ Regression coverage includes repeated cold status checks after repair, genuine
155
+ source edits, ignore-policy changes, scoped paths, symlinks, JSON eligibility,
156
+ and unusual filenames. A freshness-only development build was also verified
157
+ against SalesforceCI's approximately 903,000-file graph: repair completed, then
158
+ two separate-process status checks reported healthy and fresh without changing
159
+ repository source files or existing working-tree edits.
160
+
161
+ That acceptance establishes the reported repair/status defect, not a complete
162
+ cross-repository init/repair/status certification or a general performance gain.
163
+ Real source changes still require graph refresh. After upgrading an affected
164
+ checkout, run `knodin repair`. If it reports that content provenance is missing,
165
+ run the requested one-time `knodin index --clean`; then run `knodin status`.
166
+
167
+ ## Additional review safety fixes
168
+
169
+ Already-dirty source proofs reject symlinked paths and recheck held-file identity
170
+ and change time; an unverifiable path reports unknown rather than false freshness.
171
+ Candidate reads and promotion fences retain no-follow descriptor evidence.
172
+ Interrupted candidates with unknown/live owners or nonselected databases are
173
+ preserved rather than recursively discarded. Failed promotion rollback preserves
174
+ uncertain files and a repair-needed marker for manual inspection instead of
175
+ automatically accepting a substituted graph. These are boundary checks, not an
176
+ atomic filesystem reservation against concurrent pathname changes.
177
+
178
+ Mirror identities use the generated `repo_<20 hexadecimal characters>` format.
179
+ Malformed/unreadable registries fail closed; registry writes reject unsafe targets
180
+ and publish through an exclusive temporary file. Managed staging/source guards
181
+ refuse symlink escapes and reject an unrelated clone path before it can be cached
182
+ as that mirror. Canonical aliases remain supported, and a missing clone remains
183
+ registered only at its deterministic managed path. Mirror indexing keeps graph
184
+ state out of source but does not opt read-only mirrors into managed backup-policy
185
+ initialization or report that intentional omission as failed maintenance.
186
+ Duplicate mirror identities or canonical clone paths are rejected before routing.
187
+ Owner-proven audit-only
188
+ quarantines remain discoverable and retryable after cleanup failure; unknown or
189
+ database-bearing quarantines remain preserved.
190
+
191
+ The local final gate uses the existing npm coverage command without requiring Bun.
192
+ The privileged merge action is pinned to an immutable upstream commit.
193
+
194
+ Repair snapshot proofs now use component-wise no-follow source checks and bind
195
+ the opened descriptor to the final pathname across the complete read. Storage
196
+ policy reads similarly reject atomic pathname replacement, and applied policy
197
+ changes share the cleanup lifecycle lease so deletion cannot continue under
198
+ superseded consent.
199
+
200
+ Opportunistic global retention respects configured roots and depth and revalidates
201
+ installed consent under its deletion lease. Direct candidate cleanup refuses
202
+ read-only mirrors. Seeded worktree promotions run the same post-audit storage
203
+ finalizer, with durable retry after policy publication failure. Read-only mirror
204
+ inventory measures exact backups in resolved external state, not source decoys.
205
+
206
+ Global policy resolution and opportunistic retention share the same bounded scope
207
+ check; scheduled and opportunistic deletion share lease-time consent validation.
208
+ Mirror aliases resolve through one canonical identity seam. Mirror allocation
209
+ previews external backup quotas, while deletion remains refused. Any skipped or
210
+ unverified rollback inventory blocks managed allocation. Failed policy writes
211
+ clean their unpublished temporary files. A healthy applicable bounded global
212
+ policy can manage a new store without materializing a local policy file.
213
+ Shared-index restores and committed worktree seeds now finalize pending storage
214
+ policy and maintenance after promotion, including a failed post-promotion status
215
+ check. Release dispatch uses GitHub's exact-SHA merge precondition rather than
216
+ arming unbound auto-merge; a changed head requires fresh review and verification.
@@ -0,0 +1,78 @@
1
+ # knodin 0.14.0
2
+
3
+ Minor release for KNODIN-47, KNODIN-49, and KNODIN-50: Salesforce evidence for
4
+ static-method receivers, `instanceof` checks, platform entry points, and
5
+ unrooted Apex chains; and a corrected upgrade path for graphs indexed before
6
+ 0.13.1, whose lifecycle hooks reported success while the graph fell behind.
7
+
8
+ Pre-1.0 minor: the `class_consumers` binding version advances and the lifecycle
9
+ hook now exits non-zero on a graph the audit refuses to certify. No stored graph
10
+ is rewritten without an explicit `init` or `index --clean`.
11
+
12
+ ## Upgrade note for graphs built before 0.13.1
13
+
14
+ 0.13.1 introduced a versioned content-proof epoch and required a one-time
15
+ `knodin index --clean` for existing non-empty graphs, but nothing performed or
16
+ enforced it. On such a graph the background hook indexed the changed paths,
17
+ saw the audit refuse to certify the result, and still logged the paths and
18
+ exited 0. `status` reported lifecycle healthy with no last error while the
19
+ indexed HEAD sat dozens of commits behind. Observed on a real checkout: 33
20
+ commits behind, two days after the upgrade.
21
+
22
+ In 0.14.0:
23
+
24
+ - `knodin init` detects the missing epoch and runs the clean index itself, then
25
+ verifies health. Run it once per affected checkout; no other step is needed.
26
+ - The lifecycle hook does not migrate on its own. It fails with the audit's
27
+ first finding and remedy, records the cause in
28
+ `.knodin/hooks/last-refresh-failure`, and `knodin status` reports lifecycle
29
+ degraded with that error until `init` or `index --clean` runs. A hook on a
30
+ healthy graph is unchanged and its JSON result now includes `verification`.
31
+ - `status`, `repair`, `index`, and `init` print the audit's own first repair
32
+ step. Previously all three hardcoded `Run knodin repair`, which cannot fix
33
+ this condition; on the observed checkout the user was bounced
34
+ `status → repair → status` with an unchanged graph before finding
35
+ `index --clean` at the bottom of an unrelated error dump.
36
+ - `repair` says "Repair made no changes" when it repaired nothing and the
37
+ before/after audit counts are equal, instead of reading as progress.
38
+ - Each `.knodin/indexer.log` record carries `at` (UTC) and `event` (kind,
39
+ before/after commits, queued `sequence`), so a refresh can be tied to the Git
40
+ event that queued it. Lines remain one JSON object each.
41
+ - `IndexResult.verification` carries `repairSteps` for `--json` and MCP
42
+ consumers.
43
+
44
+ ## Salesforce evidence (KNODIN-47)
45
+
46
+ - `class_consumers` adds two roles: `static_method_receiver`, bound to the
47
+ receiving class with method arity, and `instanceof_type`. The binding
48
+ version advances from `2-interface-types` to `4-instanceof-types`. Graphs
49
+ materialized under the older binding report these roles as a repair
50
+ limitation instead of silently under-reporting them.
51
+ - The class-consumer catalog memo is deflate-packed, removing the 8 MiB cache
52
+ cliff that evicted large orgs, and the materialization digest seeks by bare
53
+ tuple.
54
+ - `explain` carries source-verified `platformEntry` evidence for Apex REST,
55
+ Flow, and platform contracts, and `dead_code` carries a bounded
56
+ `deadCodeAnalysis.platformEntries` collection (capped at 25 previews
57
+ independently of candidate pagination). Sealed artifacts read source through
58
+ a bounded, integrity-checked path; compact `explain` refuses a budget that
59
+ cannot hold the entry rather than dropping it.
60
+ - `dead_code` reports Apex method chains with no reachable root as candidates
61
+ with `candidateReason: "unrooted-chain"`, `chainIdentity`, and
62
+ `deadCodeAnalysis.rootReachability`. Response budgets protect these fields.
63
+ - Known gap: C2 REST acceptance is not green on the vendored Apex grammar
64
+ (lowercase `boolean.` parse-partial). Recorded in
65
+ `benchmarks/evaluations/knodin-41-outcomes/C2-ENTRY-EXPLANATION-ACCEPTANCE.md`.
66
+
67
+ ## Release verification
68
+
69
+ Both branches passed the full 16-shard suite locally before merge. New
70
+ regressions in `legacy-epoch-migration.spec.ts` fabricate a 0.13.0-shaped graph
71
+ (non-empty `index_state`, no `contentProofEpochV1`) and exercise the real CLI:
72
+ scoped indexing refuses and explains, `hook-refresh` exits non-zero with the
73
+ remedy, the healthy hook path still advances HEAD, `init` migrates, and
74
+ `status`/`repair` agree on the issue count for the unchanged graph.
75
+
76
+ After upgrading an affected checkout, run `knodin init`; then `knodin status`
77
+ should report healthy and fresh. `knodin index --clean` remains available as the
78
+ explicit equivalent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knodin",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "knodin": {
5
5
  "compatibility": "breaking"
6
6
  },
@@ -73,6 +73,8 @@
73
73
  "docs/releases/0.12.1.md",
74
74
  "docs/releases/0.12.2.md",
75
75
  "docs/releases/0.13.0.md",
76
+ "docs/releases/0.13.1.md",
77
+ "docs/releases/0.14.0.md",
76
78
  "docs/releases/0.3.0.md",
77
79
  "docs/releases/0.4.0.md",
78
80
  "docs/releases/0.4.1.md",
@@ -121,6 +123,7 @@
121
123
  "test:watch": "vitest --watch",
122
124
  "test:coverage": "tsx scripts/run-vitest-shards.ts --coverage",
123
125
  "scan:sonar": "npm run test:coverage && sh scripts/run-sonar-scan.sh",
126
+ "release:verify": "tsx scripts/release-final-gate.ts",
124
127
  "mirror:sync": "sh tools/hooks/sync-ghes-mirror.sh",
125
128
  "test:pack-install": "tsx scripts/pack-install-smoke.ts",
126
129
  "test:package-managers": "tsx scripts/package-manager-smoke.ts",