@ossiq/cli-linux-x64 0.1.12
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/bin/ossiq/_internal/attrs-26.1.0.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/attrs-26.1.0.dist-info/METADATA +199 -0
- package/bin/ossiq/_internal/attrs-26.1.0.dist-info/RECORD +37 -0
- package/bin/ossiq/_internal/attrs-26.1.0.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/attrs-26.1.0.dist-info/WHEEL +4 -0
- package/bin/ossiq/_internal/attrs-26.1.0.dist-info/licenses/LICENSE +21 -0
- package/bin/ossiq/_internal/base_library.zip +0 -0
- package/bin/ossiq/_internal/cel/cel.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/certifi/cacert.pem +3959 -0
- package/bin/ossiq/_internal/certifi/py.typed +0 -0
- package/bin/ossiq/_internal/charset_normalizer/cd.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/charset_normalizer/md.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/METADATA +261 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/RECORD +16 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/WHEEL +5 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/entry_points.txt +2 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/licenses/LICENSE +201 -0
- package/bin/ossiq/_internal/common_expression_language-0.10.0.dist-info/sboms/cel.cyclonedx.json +2072 -0
- package/bin/ossiq/_internal/libgcc_s.so.1 +0 -0
- package/bin/ossiq/_internal/libpython3.13.so.1.0 +0 -0
- package/bin/ossiq/_internal/libstdc++.so.6 +0 -0
- package/bin/ossiq/_internal/ossiq/data/SKILL.md +313 -0
- package/bin/ossiq/_internal/ossiq/domain/README.md +373 -0
- package/bin/ossiq/_internal/ossiq/solver/README.md +155 -0
- package/bin/ossiq/_internal/ossiq/strategy/README.md +182 -0
- package/bin/ossiq/_internal/ossiq/ui/html_templates/filter_format_highlight_days.html +7 -0
- package/bin/ossiq/_internal/ossiq/ui/html_templates/spa_app.html +18125 -0
- package/bin/ossiq/_internal/ossiq/ui/html_templates/tag_versions_difference.html +33 -0
- package/bin/ossiq/_internal/ossiq/ui/renderers/export/README.md +207 -0
- package/bin/ossiq/_internal/ossiq/ui/renderers/export/schemas/export_schema_v1.5.json +1637 -0
- package/bin/ossiq/_internal/ossiq/ui/renderers/export/schemas/export_schema_v1.5_standard.json +1164 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/METADATA +385 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/RECORD +13 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/WHEEL +4 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/direct_url.json +1 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/entry_points.txt +2 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/licenses/AUTHORS +8 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/licenses/LICENSE +661 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/uv_build.json +1 -0
- package/bin/ossiq/_internal/ossiq-0.1.12.dist-info/uv_cache.json +1 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/METADATA +171 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/RECORD +154 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/WHEEL +5 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/licenses/AUTHORS.rst +11 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/licenses/LICENSE +27 -0
- package/bin/ossiq/_internal/prompt_toolkit-3.0.53.dist-info/top_level.txt +1 -0
- package/bin/ossiq/_internal/pycard.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/METADATA +1307 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/RECORD +113 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/WHEEL +4 -0
- package/bin/ossiq/_internal/pydantic-2.13.5.dist-info/licenses/LICENSE +21 -0
- package/bin/ossiq/_internal/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/pyformula.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/pysolvers.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/INSTALLER +1 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/LICENSE +22 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/METADATA +324 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/RECORD +10 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/REQUESTED +0 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/WHEEL +6 -0
- package/bin/ossiq/_internal/semantic_version-2.10.0.dist-info/top_level.txt +1 -0
- package/bin/ossiq/_internal/setuptools/_vendor/jaraco/text/Lorem ipsum.txt +2 -0
- package/bin/ossiq/_internal/wcwidth/py.typed +0 -0
- package/bin/ossiq/_internal/wcwidth/textwrap.py,cover +664 -0
- package/bin/ossiq/_internal/yaml/_yaml.cpython-313-x86_64-linux-gnu.so +0 -0
- package/bin/ossiq/ossiq +0 -0
- package/package.json +24 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Update Strategy — the dependency update pyramid
|
|
2
|
+
|
|
3
|
+
Pure decision module that answers one question per package: *given the evidence OSS IQ has
|
|
4
|
+
gathered, which version should be recommended, and why?* It sits where `risk/` sits — it computes
|
|
5
|
+
a verdict from facts, and never touches the registry, the solver, or a `ScanRecord` directly.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌──────────────┐
|
|
9
|
+
│ cutting-edge │ + prereleases
|
|
10
|
+
┌─┴──────────────┴─┐
|
|
11
|
+
│ latest │ + widen the constraint to reach newest
|
|
12
|
+
┌─┴──────────────────┴─┐
|
|
13
|
+
│ standard │ + any drift, inside the declared range
|
|
14
|
+
┌─┴──────────────────────┴─┐
|
|
15
|
+
│ deprecation │ + end-of-life packages
|
|
16
|
+
┌─┴──────────────────────────┴─┐
|
|
17
|
+
│ security │ exploitable CVEs only, minimal diff
|
|
18
|
+
└──────────────────────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each tier is a strict superset of the one below it: every motive the lower tier admits, the higher
|
|
22
|
+
tier admits too, and it can reach at least as far up the version ladder. `security` proposes the
|
|
23
|
+
smallest possible diff that clears exploitable CVEs; `cutting-edge` proposes whatever is newest,
|
|
24
|
+
prereleases included.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Module map
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
strategy/
|
|
32
|
+
├── pyramid.py UpdateStrategy + the two tables (ADMITTED_MOTIVES, MAX_REACH) that define the pyramid,
|
|
33
|
+
│ plus the tier sets (MINIMAL_DIFF_TIERS, PRERELEASE_TIERS, MODULE_BREAK_TIERS)
|
|
34
|
+
├── motive.py UpdateMotive + PackageFacts + classify_motives() — why a package may move
|
|
35
|
+
├── targeting.py Candidate, StrategySelection, select_target() — the selector itself
|
|
36
|
+
├── compare.py compare_target() — judges a caller's chosen target against the recommendation
|
|
37
|
+
└── overrides.py StrategyPlan + parse_strategy / parse_overrides — per-run, per-package tiers
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## The two axes
|
|
43
|
+
|
|
44
|
+
A strategy is not one dial, it is two, kept separate on purpose:
|
|
45
|
+
|
|
46
|
+
- **Motive** — *why* a package is allowed to move: an exploitable CVE, an end-of-life marker, or
|
|
47
|
+
plain drift.
|
|
48
|
+
- **Reach** — *how far up the ladder* it may go on that motive: inside the declared range, inside
|
|
49
|
+
the installed major line, or anywhere.
|
|
50
|
+
|
|
51
|
+
### `ADMITTED_MOTIVES`
|
|
52
|
+
|
|
53
|
+
| Tier | exploitable_cve | end_of_life | drift |
|
|
54
|
+
|---|:---:|:---:|:---:|
|
|
55
|
+
| `security` | ✓ | | |
|
|
56
|
+
| `deprecation` | ✓ | ✓ | |
|
|
57
|
+
| `standard` | ✓ | ✓ | ✓ |
|
|
58
|
+
| `latest` | ✓ | ✓ | ✓ |
|
|
59
|
+
| `cutting-edge` | ✓ | ✓ | ✓ |
|
|
60
|
+
|
|
61
|
+
### `MAX_REACH`
|
|
62
|
+
|
|
63
|
+
| Tier | Base reach | Escalates to `LATEST` when... |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `security` | in-range | an admitted motive is `exploitable_cve` (no in-range version clears the CVE) |
|
|
66
|
+
| `deprecation` | in-range | an admitted motive is `exploitable_cve` or `end_of_life` |
|
|
67
|
+
| `standard` | in-range | same as above |
|
|
68
|
+
| `latest` | latest | already maximal |
|
|
69
|
+
| `cutting-edge` | latest | already maximal, plus prereleases admitted |
|
|
70
|
+
|
|
71
|
+
### Worked example
|
|
72
|
+
|
|
73
|
+
`pydantic==1.10.13` pinned exactly, no CVE, no maintenance signal; `requests==2.28.1`, CVEs affect
|
|
74
|
+
`2.28.1…2.31.0`, releases run through `2.34.2`:
|
|
75
|
+
|
|
76
|
+
| Tier | `pydantic` | `requests` |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `security` | no target (no CVE) | `2.32.0` — nearest CVE-clear version |
|
|
79
|
+
| `deprecation` | no target | `2.32.0` |
|
|
80
|
+
| `standard` | `1.10.13` — pin admits no in-range move | `2.34.2` — newest, already CVE-clear |
|
|
81
|
+
| `latest` | `2.13.5` — widens the pin, `requires_widening=True` | `2.34.2` |
|
|
82
|
+
| `cutting-edge` | `2.13.5` (or newer, if a prerelease exists) | `2.34.2` |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Interacting with OSS IQ
|
|
87
|
+
|
|
88
|
+
| Flag / field | Meaning |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `--update-strategy <tier>` | The default tier for the run. Default: `standard` — preserves pre-fix behaviour. |
|
|
91
|
+
| `--strategy-override pkg=tier` | Run one package at a different tier than the run default. Repeatable. |
|
|
92
|
+
| `--override pkg==version` | Force an exact version, bypassing the strategy and the solver entirely. Wins over a `--strategy-override` on the same package. |
|
|
93
|
+
| `--allow-prerelease` / `cutting-edge` | Prereleases are not a sixth rung — `cutting-edge` is `latest` plus `allow_prerelease=True`, applied at prefetch so prereleases flow into the ladder through the existing path. |
|
|
94
|
+
| `--security` (retired) | Removed, not aliased — this is pre-release software; use `--update-strategy security`. |
|
|
95
|
+
| MCP `update_strategy` / `strategy_overrides` arguments | Same semantics, over the wire, on both MCP tools. |
|
|
96
|
+
| Export `metadata.update_strategy`, `PackageMetrics.strategy_*` | The tier used for the run and the per-package verdict, in `ossiq export` / `ossiq html`. |
|
|
97
|
+
| Agent JSON `update_strategy`, per-entry `motives` / `withheld_reason` | Same verdict, in `--format agent` and both MCP tools. |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Reading the output
|
|
102
|
+
|
|
103
|
+
- **`withheld_reason`** — set only when *no* motive was admitted at the chosen tier (e.g. a package
|
|
104
|
+
with no CVE, under `security`). Names the lowest tier that would have moved it, so a project can
|
|
105
|
+
report "N more updates available under `--update-strategy standard`".
|
|
106
|
+
- **`requires_widening`** — the target sits outside the declared constraint (`IN_MAJOR` / `LATEST`
|
|
107
|
+
rung). `apply` confirms these in a separate prompt before writing them; `--yes` skips both
|
|
108
|
+
prompts.
|
|
109
|
+
- **`escalation`** — set when reach was pushed past the tier's base `MAX_REACH` (a CVE or
|
|
110
|
+
end-of-life motive escalated it), or when every reachable candidate still carries a qualifying
|
|
111
|
+
CVE and the newest was picked anyway. Never silently "stay put".
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Key Implementation Decisions
|
|
116
|
+
|
|
117
|
+
**Why two tables rather than one dial?** Collapsing motive and reach into a single ordered list of
|
|
118
|
+
five tiers would make "why did this move" and "how far could it move" the same question, and they
|
|
119
|
+
aren't: an end-of-life package can need to leave its declared range even under the otherwise
|
|
120
|
+
minimal-diff `deprecation` tier. Keeping them as two tables makes each tier's behaviour a lookup,
|
|
121
|
+
not a special case.
|
|
122
|
+
|
|
123
|
+
**Why do unscored CVEs count as exploitable, when `risk.triage` drops them?** `triage` is advisory
|
|
124
|
+
— an operator reads its verdict and decides. This module gates *writes*: recommending a version
|
|
125
|
+
that still carries an unscored CVE is a decision made on behalf of the user, and absent evidence
|
|
126
|
+
must not silently read as absent risk.
|
|
127
|
+
|
|
128
|
+
**Why is `WINDING_DOWN` not end-of-life?** It already surfaces as "Consider alternative" in
|
|
129
|
+
`next_action`, which is the right severity — a maintainer who is slowing down has not stopped.
|
|
130
|
+
Escalating it to `end_of_life` would make `deprecation`/`security` tiers move packages whose
|
|
131
|
+
maintainers are still shipping fixes, defeating the "minimal diff" promise of the bottom two tiers.
|
|
132
|
+
|
|
133
|
+
**Why minimal-diff for the bottom two tiers, freshness for the top three?** `security` and
|
|
134
|
+
`deprecation` exist to answer "what must I patch today" — the smallest change that resolves the
|
|
135
|
+
motive. `standard` and above answer "what would I install today" — freshness is the point.
|
|
136
|
+
Conflating them would make a security patch also carry unrelated drift, which is exactly the kind
|
|
137
|
+
of unreviewable diff this module exists to prevent.
|
|
138
|
+
|
|
139
|
+
**Why no fourth `PRERELEASE` rung?** `RecommendationRung` and the version ladder are about *where*
|
|
140
|
+
on the ladder a version sits (in-range / in-major / latest), which is orthogonal to whether
|
|
141
|
+
prereleases are visible on that ladder at all. Modelling prerelease admission as `allow_prerelease`
|
|
142
|
+
at prefetch time — exactly like `--allow-prerelease` already works — means `cutting-edge` needs no
|
|
143
|
+
changes to `VersionLadder`, `RecommendationRung`, or the export schema.
|
|
144
|
+
|
|
145
|
+
**Why is `Candidate` pre-tagged rather than parsed here?** Keeping version parsing, constraint
|
|
146
|
+
matching and registry lookups entirely in `service.project.strategy.build_candidates` means this
|
|
147
|
+
module has zero registry coupling and is trivially property-testable: a `Candidate` list is just
|
|
148
|
+
data, and `select_target` is a pure function over it. A release's age rides along the same way, as
|
|
149
|
+
`Candidate.age_days`: the tag is a registry fact, the threshold (`cooldown_period`) is a parameter,
|
|
150
|
+
and the decision stays here.
|
|
151
|
+
|
|
152
|
+
**Why does the cooldown live in the selector rather than in `plan`/`apply`?** It used to live only
|
|
153
|
+
there, as `service.update.is_held_for_cooldown`. But `apply_update_strategy` runs last and rewrites
|
|
154
|
+
`recommended_version`, so `status` would recommend a two-day-old release with "Update Immediately"
|
|
155
|
+
while `apply` refused it — with a settled release sitting between the two, recommended by nobody.
|
|
156
|
+
Filtering fresh candidates out of the ladder here makes one decision serve every surface, which is
|
|
157
|
+
the whole point of rule 5 in the root `CLAUDE.md`. The downstream hold survives as a backstop for
|
|
158
|
+
transitive recommendations, which come from the solver's *soft* freshness penalty.
|
|
159
|
+
|
|
160
|
+
**Why may only `latest`/`cutting-edge` cross to ESM-only, and only on a `require(esm)` Node?**
|
|
161
|
+
For a CommonJS project an ESM-only release is a break whatever its version number:
|
|
162
|
+
`require()` of it fails outright below Node 20.19/22.12, and above that it returns the module
|
|
163
|
+
namespace, so a default-export-only package (chalk) still breaks. `MODULE_BREAK_TIERS` makes
|
|
164
|
+
crossing a freshness decision the top of the pyramid may take, flagged with `breaking_change` so
|
|
165
|
+
`apply` asks for it separately. Everything below stays on the installed module system. The
|
|
166
|
+
permission only grows with the tier, so rule 6 ("a higher tier's target is never lower") still
|
|
167
|
+
holds by construction. The gate itself (`service.project.strategy.module_system_gate`) is a
|
|
168
|
+
*strict* gate: `build_candidates` never waives it just because it empties the ladder, because a
|
|
169
|
+
drift-only package should stay put rather than cross. `apply_update_strategy` waives it in one
|
|
170
|
+
case, when an `exploitable_cve` or `end_of_life` motive has no clean candidate left without it.
|
|
171
|
+
It re-runs `select_target` over the full ladder and keeps that answer only if it resolves the
|
|
172
|
+
motive. The selector stays pure; the re-run is wiring.
|
|
173
|
+
|
|
174
|
+
**Why does `compare_target` never pick a version?** `recommended_version` has one writer. A
|
|
175
|
+
second target-picker for `update_context` would be a second answer to the same question, and the
|
|
176
|
+
two would drift. `compare_target` only turns facts about a proposed target into a verdict relative
|
|
177
|
+
to that one answer.
|
|
178
|
+
|
|
179
|
+
**Why `--strategy-override` and not `--override-strategy`?** `--override pkg==version` already
|
|
180
|
+
exists and means "force this exact version". `--override-strategy` reads as a variant of the same
|
|
181
|
+
flag and invites the wrong mental model; `--strategy-override` parses unambiguously as "an override
|
|
182
|
+
of the strategy" for one package.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{% if duration_days == 0 %}
|
|
2
|
+
<strong class="text-green text-semibold">{{ formatted_string }}</strong>
|
|
3
|
+
{% elif threshold_days and duration_days and duration_days > threshold_days %}
|
|
4
|
+
<strong class="text-red-700 dark:text-red-500 text-semibold">{{ formatted_string }}</strong>
|
|
5
|
+
{% else %}
|
|
6
|
+
<strong class="text-semibold">{{ formatted_string }}</strong>
|
|
7
|
+
{% endif %}
|