7 minutes read
Decision records
These records explain the choices behind Offing's architecture, data format, contracts, and policy. Each one is accepted unless a later record supersedes it, or unless its status says otherwise. Records are numbered in the order they were written; this page groups them by theme.
Detectors
| # | Title | Decision |
|---|---|---|
| 0002 | Add Go as a first-class detector ecosystem | Add evidence-backed Go runtime, toolchain, and direct-module lifecycle detection through the existing detector SDK, without requiring the Go toolchain or network access at scan time. |
| 0007 | Detect end-of-life container base images from Dockerfiles and Compose files | Add a container detector that reports base-image references whose release line has a vendor-published end of life. |
| 0008 | Detect removed and deprecated Kubernetes APIs in manifests, Helm charts, and kustomizations | Add a kubernetes detector that reports apiVersion/kind pairs the Kubernetes project has documented as deprecated or no longer served. |
| 0009 | Detect end-of-support JDK releases and relocated Maven dependencies in JVM projects | Add a java detector for pinned or targeted JDKs and Maven coordinates that a vendor or maintainer has documented as out of support or moved. |
| 0011 | Detect unsupported Rust toolchains and abandoned or yanked crates | Add a rust detector that reads Cargo and toolchain files and reports lifecycle risks an authoritative source states. |
| 0012 | Detect unsupported .NET targets and deprecated NuGet packages | Add a dotnet detector for unsupported .NET SDK/runtime lines and deprecated NuGet packages with publisher evidence. |
| 0013 | Detector maturity levels and the evidence to move between them | Use three levels — experimental, beta, and stable — with fixed evidence floors (fixtures, corpus size, precision, recall) to move between them. |
| 0014 | Detect retired CI runner images | Add a ci-images detector that reports only image lifecycle risks the platform's authoritative documentation explicitly states. |
| 0015 | Corpus fetch strategy: sparse checkout, concurrency 16 | Default the public-repository corpus to sparse checkout at concurrency 16, with cache invalidation keyed to detector input declarations. |
Data and contracts
| # | Title | Decision |
|---|---|---|
| 0004 | Key lifecycle rules by PURL and VERS with provenance | Move every ecosystem to one offing.registry.v2 format keyed by Package URL and VERS, with provenance on each rule. |
| 0005 | Draft lifecycle rules with importers; never write the registry | Let importers draft rules from one authoritative source; a person reviews every draft and changes registry/ in a pull request. |
| 0006 | Ship lifecycle data as a versioned, content-addressed bundle | Publish and consume lifecycle data as an offing.data-bundle.v1 document separate from the code release. |
| 0010 | Record announced end-of-life dates that have not happened yet | Add a scheduled-end-of-life event for announced future ends, reported at low severity and promoted by a reviewed change after the date. |
| 0016 | CLI exit codes and error codes | Map failures to five fixed classes and exit codes: policy 1, usage 2, data 3, parse 4, internal 5. |
| 0017 | Do not read OpenEoX documents yet; keep CLE as the interchange target | Scans never read OpenEoX; do not ingest it until the Core Schema is final, and keep CLE as the standards interchange path. |
| 0020 | License the lifecycle dataset under CC BY 4.0 | License registry/ and first-party schemas in the data-only package under CC BY 4.0; keep the rest of the repository Apache-2.0. |
| 0022 | Match Python TestCase method calls with a method shape |
Add a Python-only method symbol shape that matches a call by name on any receiver inside a class that directly subclasses unittest.TestCase or an alias of it. |
| 0028 | Publish a self-contained MCP output schema | Inline every cross-file $ref before the MCP server advertises a tool's schema and throw when one cannot resolve; same-document references stay, and sibling keywords win over the inlined target. |
| 0032 | Split the MCP server into one file per tool | Give each MCP tool its own file under src/mcp/tools/, list them in a small registry, and prove with a tools/list snapshot that no advertised name, description, input schema, output schema, or annotation changed. |
| 0033 | Require the retrieval date on every lifecycle source | Make sources[].accessedAt required in registry v2, backfill every bundled record, and let the importer runner stamp the run date so every finding shows when its source was read. |
| 0037 | Recommend a release line from bundled data only | Add recommend_version and offing recommend: the newest unended line whose end date is at least 180 days away, from bundled rules only; no-data is explicit and never says "latest". |
| 0036 | Add Deno and Elixir to the public validation schema | Add deno and elixir to the published corpus ecosystem enum when those detectors gain a public corpus. The change is additive. |
| 0034 | Publish the detector limits in the SDK | Export DETECTOR_LIMITS and its DetectorLimits type so the file-size, depth, and vendored-directory bounds are part of the typed detector contract and conformance reads them from there. |
Adoption
| # | Title | Decision |
|---|---|---|
| 0018 | Ship v0.8.0 (adoption) and v0.9.0 (visibility) before v1 | Ship adoption then visibility before v1, with usage before attention, and record post-v1 ideas as unscheduled epics. |
| 0019 | Privacy-first adoption metrics; opt-in telemetry only after v1 | Keep product telemetry off by default forever as the default; any opt-in comes only after a separate post-v1 decision. |
| 0021 | Ship adoption and visibility as one release, with one launch | Merge v0.9.0 into v0.8.0 and replace the three mini-launches with one launch that follows the release. |
Quality
| # | Title | Decision |
|---|---|---|
| 0023 | Define reference quality as measurable, enforced criteria | Proposed. Define eight quality criteria, each enforced by a failing check; zero new debt; existing debt allowlisted with an owner and an expiry of 2027-01-26. |
| 0025 | Choose the linter and formatter | Lint with ESLint and typescript-eslint (type-aware); format with Biome; do not use Biome for typed lint or Prettier for format. |
| 0026 | Document the public API and enforce it with lint | Put a behavior-focused doc comment on every public declaration and enforce it with a local ESLint rule; no contract semantics change. |
| 0027 | Keep fixtures out of scanners' sight with neutral names | Commit manifest-shaped fixtures under a .fixture suffix and restore the real name in a temporary directory at test time; reject archives and dismissals; scanner config is a narrow fallback. |
| 0029 | Cut scanner complexity by extracting the line masker and replacing branch chains with tables | Move the per-language literal scanner to src/usage/masking.ts, dispatch readers on tables, and prove parity differentially against a build of main; the SDK export surface is unchanged. |
| 0030 | Select corpus repositories independently of results; target 200 per ecosystem | Freeze eligibility and stack variety for all 23 buckets before scanning; retain poor results and publish honest shortfalls against 200 reviewed repositories per bucket. Accepted on maintainer merge of the PR for #2116. |
Operations
| # | Title | Decision |
|---|---|---|
| 0024 | CI lanes: pull request, merge queue, main, nightly, release | Proposed. Five CI lanes with wall-clock budgets; one required check (ci-ok); the merge queue is the proof that lands on main. |
Policy
| # | Title | Decision |
|---|---|---|
| 0001 | TypeScript-first, local-first architecture | Build v0.1 in TypeScript on Node.js 22 or newer, sharing one language across CLI, engine, detectors, Action, and MCP, with no hosted backend. |
| 0003 | Generate host plugins from shared Offing assets | Keep Skill, runtime, rules, and migration behavior as canonical shared sources and generate Codex and Claude plugins from them so CI fails on drift. |
| 0031 | Layered architecture and the allowed-imports table | Proposed. Seven layers (domain, SDK, core, detectors, services, reporters, adapters) with one allowed-imports table and runtime-access rules; each epic #1990 follow-up adds its rule to the checker. |
| 0035 | One error model with stable reason codes | Every failure carries one reason code that fixes its class and exit code; the CLI error document gains error.reason and MCP failures carry the code in _meta.offing. |