Turn your generation records into answers about rights, provenance, and reuse.
Prompt extraction, PNG info, and A1111 / ComfyUI metadata — normalized into one record, then answered.
You have generated images. Somewhere you also have a model ledger, a licence sheet, and — if you
are lucky — the settings each image was made with. genrecord puts those into one shape and
answers four questions about every image you own, in bulk, offline.
- May I use this commercially? Traced through every checkpoint and LoRA, against a licence catalogue you supply. Unknown is never rounded to yes — that is a property of the code, not a score.
- Can I explain how it was made? Provenance is a different question from reproducibility, and they disagree more often than you would expect.
- Can I make it again? And when the answer is no, which kind of no — never recorded, not identifiable, or simply not installed.
- Can I make something close to it? Which knobs are worth turning, and which cannot possibly change the picture.
No ComfyUI. No GPU. No plugin. Zero dependencies, and no network access — it never contacts a third-party service, so it brings no credentials, rate limits, or someone else's terms of service into your stack.
import { emptyRecord, assessCommercialUse, createLicenseCatalog, USE } from 'genrecord';
const record = emptyRecord(); // fill from your own logs / ledger …
record.model.name = 'someCheckpoint';
record.model.sourceId = '12345';
const catalog = createLicenseCatalog(myLicenceSheet);
const { verdict, blockers } = assessCommercialUse(record, catalog, { use: USE.IMAGE_SALE });
// verdict: 'allowed' | 'forbidden' | 'blocked' | 'unknown'Not "faster" — these are things that are not possible without a normalized record.
| Today, without it | With it | |
|---|---|---|
| Clearing a back catalogue | Someone opens images one at a time and guesses from filenames. Nobody finishes 40,000. | Every image lands in allowed / forbidden / blocked / unknown, and the unknown pile is an explicit work queue rather than an assumption. |
| Answering "how was this made?" | The person who made it has left, and the settings were on their laptop. | The record says what it can attest to and — separately — names the gaps, so "we don't know" is a documented finding rather than a silence. |
| Knowing when to stop looking | A model is missing, so someone spends a day hunting for it. | Missing resources split into not-recorded / not-identifiable / not-present. The first two mean stop; only the third is worth a search. |
| Salvaging dead assets | "Can't reproduce it" ends the conversation and the asset is written off. | Reproducibility and reusability are separate verdicts. In the reference library, 341 of 346 could seed a derivative while only 300 were judged runnable — including 22 of the 46 that were not. |
| Not burning GPU on nothing | A parameter sweep runs overnight and every frame comes back identical. | Axes with no possible effect return no plan at all, with the evidence attached. |
| Auditing at portfolio scale | There is no number to report, so the answer to "what proportion of our assets are clear?" is a shrug. | summarizeCorpus produces the distribution across the whole library — and counts unknown as its own category rather than folding it into either side. |
Every one of those is a call you make in code. There is no UI, no CLI, and no server — see What you build on top below.
genrecord answers questions about a record. How complete that record is decides how much it
can tell you, and the three ways of getting one are not equivalent. This is the single most
important thing to understand before evaluating it.
| Input path | Checkpoint & LoRA identity | Licence lookup key | Model family known | Verdict quality |
|---|---|---|---|---|
Your ledger / generation logs — fill emptyRecord(), or fromLoraManagerRecipe() |
hash · versionId · sourceId · name | all four, most specific first | yes (baseModel) |
full |
| ComfyUI workflow image | names, read from the graph | name only | no | usable, name-matched |
| A1111-style image | name only; LoRAs come from <lora:…> in the prompt |
name only | no | usable, name-matched |
Measured against the author's real ComfyUI output folder on 2026-08-19 — 2,896 images before the extractor landed, 2,928 after (it is a live folder; the percentages are what to read, and every "before" figure was 0.0%):
| Field | Before | After |
|---|---|---|
model.name |
0.0% | 99.2% |
prompt.positive |
0.0% | 98.9% |
seed · steps · cfg · sampler · scheduler |
0.0% | 99.9% |
image.width |
0.0% | 99.9% |
adapters (LoRA) |
0.0% | 81.6% |
| record sufficiency | partial ×2,894 |
sufficient ×2,841 |
The extractor refuses rather than guesses. A field is filled only when the graph answers it
unambiguously, and every field left empty carries a machine-readable reason in
provenance.unreadKeys — 55 images had a core field missing, and all 55 said why.
Concretely: a graph with two checkpoint loaders fills no model name; two independent samplers
fill no sampling values. Positive and negative prompts are resolved by following the sampler's
positive / negative links, never by node order — ordering guesses wrong on the 4.3% of
graphs that do not have exactly two text nodes.
Two limits remain, and they change what the tool is worth to you:
model.baseModelcannot be filled from an image at all — neither A1111 parameters nor the graph carry it. Everything keyed on model family therefore goes quiet on the image path: the CLIP-Skip result below does not fire, andcheckSubstitutionreturnsnull.- Every headline number elsewhere on this page was measured on ledger-derived records, via
fromLoraManagerRecipeon recipe JSON already enriched with hashes andbaseModel. The image path is now measured end-to-end for extraction (the table above), but a resource read from a graph carries a filename and nothing else — no hash, no distributor id — so licence lookups still fall back to name matching.
So the honest positioning is: genrecord is for organisations that already hold a model
ledger or generation logs — that is the path where resources carry hashes and distributor ids,
which is what licence matching actually needs. Reading the image directly is a recovery path
for assets whose ledger entry was lost. It now recovers most of the settings, and still only
ever recovers names.
Measured 2026-08-19 on synthetic records, Node 22.17, one core, no I/O:
| Operation | 10,000 records | 100,000 records |
|---|---|---|
assessRecordSufficiency |
19 ms | 79 ms |
judgeReplayability (with environment) |
52 ms | 339 ms |
assessCommercialUse |
29 ms | 151 ms |
buildProvenanceReport |
43 ms | 327 ms |
surveyAxes |
47 ms | 387 ms |
summarizeCorpus (whole library, one pass) |
65 ms | 558 ms |
| resident memory | 167 MB | 812 MB |
recordFromImage (A1111 PNG, in memory) |
14.8 µs each | — |
A hundred thousand assets clear all four questions in about a second. The cost is not compute — it is assembling the catalogue.
Note the memory column: summarizeCorpus takes an array, and records deliberately keep their
original payload (raw). Batching a library that does not fit in memory is the caller's job.
Some images cannot be reproduced no matter what you install, because the information was never written down. This one does not decay: you cannot restore what was never recorded. It does not depend on the implementation, the environment, or the version — it is the definition doing its work.
In the reference library it flagged 26 such images. The reference implementation independently
classified all 26 as unavailable. No image was generated for any of them — this is agreement
between two readings of the same absent fields, not a hit rate. The two are not fully independent:
both detect the same missing skeleton.
Give it a licence catalogue and it walks the whole recipe — checkpoint, every LoRA, including LoRAs written inline in the prompt text that never appear in a resource list.
The guarantee is structural, not statistical. Aggregation follows the weakest constituent,
ranked blocked > forbidden > unknown > allowed; a resource absent from the catalogue returns
unknown; an empty recipe returns unknown, not "nothing wrong". There is no path in the
code by which an unresolved resource becomes allowed. In a 328-image cross-check against an
independently built classification, unsafe-side disagreements: 0.
The proportion that comes back clear is a property of your library, not of this code. A collection of licensed studio models and one scraped from a hobbyist site will not land anywhere near each other, so a number measured here would anchor you to the wrong figure.
import { createLicenseCatalog, assessCommercialUse, USE } from 'genrecord';
const catalog = createLicenseCatalog(myLedger); // you supply this
const { verdict, blockers } = assessCommercialUse(record, catalog, { use: USE.IMAGE_SALE });USE distinguishes IMAGE_SALE (sell the output), SERVICE (run a generation service),
and MODEL_RESALE (redistribute the model) — the same model can answer differently for each.
Only IMAGE_SALE has been validated against ground truth; the other two map permission names
without a reference to check against.
The guarantee covers what could not be looked up — not what was looked up weakly. Aggregation
protects you from a resource the catalogue does not contain; it says nothing about a resource that
matched on a filename, which cannot distinguish two different files that share a name. Each
constituent therefore reports identity: {hash, versionId, sourceId, inline} — which keys the
record actually carried — alongside matchedBy, and weaklyIdentified lists the allowed
constituents that got there on a name alone. That is not a smaller number; it is the subset a
person has to look at. On records read back from images it is everything: measured across
2,965 ComfyUI images, all 11,656 resolved constituents matched by name, because an image
carries no hash and no distributor id to match on.
The catalogue is the expensive half. Shipping a verdict engine and saying "you supply the licence data" hands back the harder problem, so there is an importer for the one public source whose terms allow it:
import { fromHuggingFaceModels, createLicenseCatalog } from 'genrecord';
const models = await Promise.all(ids.map(id => // ← you fetch; this package never does
fetch(`https://huggingface.co/api/models/${id}`).then(r => r.json())));
const { entries, summary } = fromHuggingFaceModels(models);
const catalog = createLicenseCatalog(entries);Hugging Face returns a model card's license from an endpoint that needs no account. Civitai is
deliberately not supported: its terms restrict use of the Service to personal, non-commercial
use, so whether a catalogue derived from it may be used commercially is a separate judgement — and
mixing a source you can use with one you may not is exactly the kind of thing this package refuses
to do quietly.
By default the importer only ever makes verdicts stricter. A licence identifier that is
explicitly non-commercial becomes forbidden; everything else stays unknown, because:
- A licence identifier is a declaration, not a verified fact.
- A model's licence governs the model, not automatically the outputs. OpenRAIL licences
say so explicitly;
apache-2.0says nothing about outputs at all. "No restriction written down" is not "permission granted."
If you want the permissive side filled in too, { assumePermissive: true } does it — and returns
summary.assumedPermissiveLicenses, the identifiers it read that way, so your counsel can
check the interpretation rather than inherit it. Identifiers in neither list come back in
summary.unrecognizedLicenses rather than being folded silently into unknown.
The catalogue is not bundled. Permissions change after the fact, redistributing a distributor's
data is its own licensing question, and every organisation draws its own "we don't touch that"
line. So you pass it in, along with an optional blocklist for policy that has nothing to do with
what the distributor permits.
Provenance and reproducibility are not the same axis. An img2img output can be perfectly reproducible — the graph survived intact — and still be unexplainable, because the input image it was built from was never recorded.
Four images in the library came back broken for exactly that reason. One of them was
commercially cleared. Being licensed is not the same as being explainable.
Scope: the check keys on LoadImage-class nodes in an embedded workflow, so it detects
ComfyUI img2img. A1111 img2img has no graph and is not detected.
Generation is slow and it crashes. Before you sweep an axis, it is worth knowing whether that axis
can do anything at all. planSweep returns no variants for a dead axis, with the reason
attached — rather than letting you find out after the GPU time is spent.
| Axis | Called inert | Why |
|---|---|---|
| CLIP Skip on SDXL-family models | all 291 SDXL-family images | Architectural: SDXL already uses the penultimate layer. Confirmed by a same-seed A/B that came back pixel-identical. 291 is a population count, not 291 measurements. |
| denoise with nothing to apply it to | 28 images | No input image and no upscale stage in the record |
Assuming an 11-point sweep of those axes across every affected image, that is 3,509 generations not run — an upper bound under an assumption, not an expectation.
It also refuses to call a cross-family substitution safe. Applying a karras schedule to a
flow-matching model produced a near-flat image at the same seed — so checkSubstitution returns
false across families, and null when it cannot identify the family at all. Not true.
Unknown is not safe.
Both rows require model.baseModel, which only ledger-derived records carry. On image input
the family is unknown, so this section goes quiet rather than guessing: assessAxis returns
unknown, checkSubstitution returns null, and a sweep is planned anyway. You simply do not
get the saving.
That behaviour is newer than this paragraph. Until 2026-08-19 an unknown family returned
effective — asserting the axis mattered while its own reason field said the family could not
be identified. An SDXL image read from a file was therefore told to sweep an axis that provably
does nothing: the exact failure this feature exists to prevent, inverted. It is now pinned by a
test that holds across every axis — no verdict may be assertive while its own reason states the
evidence is absent.
genrecord is a library. It has no CLI, no daemon, no UI, no scheduler, and no storage. These
are yours to write, and they are usually a few hundred lines:
- The catalogue adapters — your model ledger →
createEnvironmententries; your licence sheet →createLicenseCatalogentries. This is the real integration cost. - The walk — iterating a directory, a DAM, or a database.
summarizeCorpustakes the array once you have it. - Batching and persistence — verdicts are computed, never stored.
- The policy —
blocklistis a function you write, because "we don't touch that" is yours.
What each side must supply:
createEnvironment(entries) |
createLicenseCatalog(entries) |
|---|---|
kind ('model' | 'adapter') |
hash · sourceId · versionId · name — any subset; lookup tries them most-specific first |
name |
permissions — array, or null for "unknown" (an empty array means nothing is permitted, which is not the same thing) |
hash (optional; enables hash matching) |
blocked / blockedReason (optional) |
path (optional) |
baseModel · title · sourceUrl (optional) |
scanModelDirectories([{dir, kind}]) builds the environment side by walking your model folders —
but the entries it produces carry no hash, so that environment matches by name only. If you
need hash matching, compute the digests yourself and pass them to createEnvironment. This
package does not hash files.
Licences change after the fact. When an image that was allowed last year comes back
forbidden this year, the only question that matters is what moved — the record, your
catalogue, or this library's rules. Without that split, the answer is "re-review everything".
buildEvidenceRecord returns the four verdicts wrapped in what they were computed from:
a digest of the record, your catalogue's own fingerprint and the label you gave it, the
environment's fingerprint, whether a blocklist was supplied at all, and a rulesVersion.
diffEvidence(before, after) then attributes the change.
const before = buildEvidenceRecord(record, { catalog: lastYear, catalogInfo: { label: 'ledger', asOf: '2025-08-01' } });
const after = buildEvidenceRecord(record, { catalog: today, catalogInfo: { label: 'ledger', asOf: '2026-08-01' } });
diffEvidence(before, after);
// { transitions: { license: { from: 'allowed', to: 'forbidden' }, … },
// attribution: ['catalog'], // ← not 'record'
// notes: [...] }Measured on 1,200 real ComfyUI images with ten permissions withdrawn from the catalogue:
370 moved allowed → forbidden, and all 1,200 were attributed to catalog — the images
did not change. Building the evidence costs 249 µs per record and diffing 11 µs.
Four things it deliberately does not do:
- It has no clock.
assessedAtis a value you pass in. A clock insidesrc/would make the verdicts non-deterministic, and determinism is worth more than convenience here. - It does not fetch or notify. This is a pure function over two evidence records. Calling it a watchdog would be a maintenance promise, and none is offered.
- It does not put everything in one hash. One hash would mean every row in your ledger changes the day a single line of this library changes, and the diff becomes all noise.
- When inputs changed but the verdict did not, it says so — rather than letting you read that as proof the change had no effect. That misreading has happened in this project's own measurements, which is why the note is in the output rather than in a document.
The digest is not cryptographic (node:crypto is not imported, so the verification procedure
in NOTICE stays three builtins wide). It detects that an input changed. It does not
prove nobody rewrote it — consistent with the fact that record authenticity is never verified.
Two questions come up first in any audit, and both are answerable by reading the code rather than by trusting a measurement — so neither depends on a population or on a reference implementation.
The same input always produces the same answer. There is no clock, no randomness, no
environment lookup, and no cryptographic seed anywhere under src/:
grep -rnE "new Date|Date\.now|Math\.random|process\.env|'node:crypto'" src/ # prints nothing
It writes nothing and calls nothing. No file is created or modified, no process is spawned,
no socket is opened. Its entire interaction with the outside world is four read-only calls —
readFile twice, readdir and stat once each — and the only Node builtins it imports are
fs/promises, path, and zlib:
grep -rnE "writeFile|appendFile|mkdir|unlink|rename|fetch\(|'node:child_process'" src/ # prints nothing
The module specifiers are quoted in the patterns on purpose: an unquoted node:crypto also
matches the sentence in digest.mjs explaining why it is not imported. A published command that
prints a line while claiming to print none is worse than no command — this project has now shipped
that mistake three times, so the two commands above are themselves extracted from this file and
run by the test suite.
Both are held by tests, not just by this paragraph — npm test fails if a single Date.now()
or writeFile appears under src/, and the import allowlist is asserted rather than merely
scanned for forbidden words. Adding a new Node builtin turns it red even if that builtin is
harmless, because the point is that the list stops being reviewed once it stops being pinned.
They are orthogonal, and the difference decides whether you need both.
| C2PA / Content Credentials | genrecord | |
|---|---|---|
| Question | Was this file signed, and is it unmodified since? | Which models made it, and do their licences permit your use? |
| Mechanism | Cryptographic signature attached at creation | Reading the generation record and walking the recipe |
| If nobody signed it | Nothing to check | Still works — the settings are in the file, or in your logs |
| Authenticity | That is the whole point | Never verified. It reports what a record claims |
| Licence of a LoRA | Says nothing | The entire subject |
C2PA is royalty-free and backed by a large consortium, so if you can get signed provenance, get it — it answers a question this package explicitly does not. But a signature does not tell you whether the checkpoint you used forbids commercial output, and locally-run open models with community adapters usually carry no manifest at all. Nothing here competes with C2PA; the two cover different failure modes, and having one does not give you the other.
genrecord answers |
You answer | |
|---|---|---|
| Licence terms | Whether the stated permissions, applied to this recipe, permit this use — and which constituent blocks it | Whether the stated permissions are correct and current. Whether to accept the risk |
| Licence data | Nothing. It ships no catalogue at all | Supplying and maintaining the catalogue |
| Provenance | What the record claims, and precisely where it is silent | Whether the record is truthful — authenticity is never verified; metadata can be rewritten |
| Reproducibility | Whether the recorded resources resolve against the environment you supplied | Whether the resulting image is acceptable. "Runnable" is not "identical" |
| Obtainability | Nothing reliable — see the limits below | Procurement |
| Legal position | Nothing. This is not legal advice | The judgment |
| Operation | A library call | Everything around it: iteration, storage, scheduling, UI, access control |
Every figure here comes from one developer's library of 346 images, measured in August 2026. Your distribution will differ, sometimes a lot. That caveat applies to the whole page; it is stated once here rather than after each number.
The figures divide into three kinds, and they behave differently:
| Kind | Examples | What moves it |
|---|---|---|
| Structural | "unknown is never rounded to yes" · "you cannot restore what was never recorded" · "unidentified family returns null, not true" |
Nothing. These are properties of the code, provable by reading it |
| Population | 298 / 22 / 26 · 291 SDXL-family · 341 can seed a derivative · 3,509 | Your library. Add or remove assets and these change |
| Agreement | 98.9% resolution match · 80.9% verdict match · 10.9% false "yes" · 94.0 / 90.9 / 0.0% "reproduction reached" · 32.7% zero-guesswork | The reference implementation, which is still being improved. These move without a line of this code changing |
The last row deserves care. Those figures are agreement with a separate implementation, most
of them against a snapshot frozen on 2026-08-11. In particular, validate.mjs computes
"reproduction reached" as level !== 'unavailable' from that snapshot — it is a label, not a
generation run. Where earlier versions of this page said images were "in fact unreproducible",
the accurate statement is that both implementations read the same missing fields and reached the
same conclusion.
Of the 300 the reference judged runnable, 67 were reported as visibly degraded on human review. Runnable is not identical.
- "Reproducible" is not "identical". Only 32.7% of the library carried no risk-class warning, as classified by the reference implementation on 2026-08-11.
- One declared target is not met. Whether a missing model can still be obtained is not
something an image can tell you. Against a stated goal of under 3% false "yes", the measured
disagreement is 10.9%, against the reference implementation's output as recorded on
2026-08-11. The cause is structural rather than a tuning problem — the signal needed to close it
is not present in the record — so read it as a limit of the approach, not as a defect awaiting
a fix. It is disclosed rather than hidden, and no timeline is implied.
How to operate around it: treat
not-presentas "worth a search", never as "obtainable". The three-way split of why a resource is missing is sound; the obtainability judgment is not. - Record authenticity is never verified. Metadata can be rewritten. This reports what a record claims and where it is silent.
- This is not legal advice. It mechanically applies permissions a distributor stated and prohibitions you stated. The judgment remains yours.
Be clear about which is which, because it changes how much the numbers above are worth to you.
Runs anywhere — no data, no dependencies:
npm test # 155 tests, on Node.js 20 or newer
This covers the decision boundaries on synthetic fixtures, and the integrity of what is distributed
here: no NUL bytes, no external imports, LICENSE is the real AGPL text, package.json agrees
with it. These are the tests that hold you to your word.
Needs the author's library — you cannot reproduce these:
npm run validate # record sufficiency against the reference's labels
npm run validate:license # licence verdicts against an independent classification
npm run validate:variation # derivation population and inert axes
npm run validate:corpus # portfolio-level health
Every measured figure on this page comes from these, and they read 346 recipes, a label set produced against a running ComfyUI, and a licence ledger — none of which ships here. It is someone's personal library, and part of it is data obtained from a distributor.
The scripts are included anyway, because how a number was produced is more useful than the
number. They will stop with an explanation rather than a stack trace, and they accept
LM_RECIPE_DIR / LM_LICENSE_DIR if you want to point them at your own.
Dual-licensed.
- AGPL-3.0 for open use — see LICENSE. Note that the AGPL's obligations extend to network use, so embedding this inside a closed product or hosted service generally requires releasing that work under the same terms.
- A commercial licence for everyone that cannot accept those terms — as a term licence or a perpetual one. See COMMERCIAL.md. Neither includes maintenance, updates, or support, which is stated there plainly rather than buried. The perpetual form exists so that you need not depend on the author for any of it.
NOTICE documents why this package can be licensed this way at all, and — more usefully — how to verify that claim yourself rather than taking it on trust.
Commercial licensing: syugoji@gmail.com (English or Japanese). What the two forms cover — and what they deliberately do not — is in COMMERCIAL.md.
Bugs and questions about the open version belong in the issue tracker rather than in email, so that the answers stay where the next person will look for them.
Pull requests are not being accepted yet. Dual licensing requires that one party hold the copyright to all of it; accepting outside contributions without a contributor licence agreement in place would quietly make the commercial licence unofferable. The CLA is not ready. Issues and bug reports are welcome in the meantime.
生成AIで作った資産について、「商用に使ってよいか」「どう作られたか説明できるか」「もう一度出せるか」 「どこを変えれば近いものが作れるか」の4つを、生成環境を立ち上げずに全数へ機械で答える部品です。 ComfyUI も GPU もプラグインも要らず、依存パッケージはゼロ、外部サービスへのアクセスもありません。 資格情報もレート制限も第三者の規約も持ち込みません。
| いま | 導入後 | |
|---|---|---|
| 過去資産の権利棚卸し | 1枚ずつ開いてファイル名から推測する。4万件は終わらない | 全件が 可/不可/禁止/判定不能 へ落ち、判定不能が明示的な作業待ち行列になる(暗黙の仮定でなくなる) |
| 「どう作られたか」への回答 | 作った本人が退職し、設定はそのPCにしかなかった | 説明できる範囲と、欠落そのものを名指しで出す。「分からない」が沈黙でなく記録された所見になる |
| 探すのをやめる判断 | モデルが1本足りず、1日かけて探す | 不足が 記録が無い/同定できない/導入されていない の3つに割れる。前2つは探しても無駄で、探す価値があるのは3つ目だけ |
| 死んだ資産の救出 | 「再現できない」で会話が終わり、資産ごと捨てる | 再現可否と再利用可否は別の判定。参照ライブラリでは実行可能と判定されたのが300件に対し、派生の土台になるのは341件(再現不可46件のうち22件を含む) |
| 無駄なGPUを焼かない | 一晩掃引して全フレーム同一だった | 効きようがない軸は計画そのものを返さない(根拠つき) |
| 資産全体の健全度 | 報告できる数字が無く、「うちの資産の何割が安全か」に肩をすくめるしかない | summarizeCorpus が全体分布を出す。unknown を可否のどちらにも寄せず独立に数える |
いずれもコードから呼ぶ形です。 CLI も画面もサーバもありません(下記「利用者が書く層」)。
これが評価前に最も重要な前提です。
| 入力経路 | 構成の同定 | 許諾の照合鍵 | 系統 | 判定の質 |
|---|---|---|---|---|
御社の台帳・生成ログ(emptyRecord() を埋める) |
hash・versionId・sourceId・名前 | 4つすべて(具体的な順に試行) | 判る | 完全 |
| ComfyUI ワークフロー画像 | グラフから名前を読む | 名前一致のみ | 判らない | 名前一致まで |
| A1111 形式の画像 | 名前のみ。LoRA はプロンプト本文の <lora:…> から |
名前一致のみ | 判らない | 名前一致まで |
作者の稼働中の ComfyUI 出力フォルダでの実測(2026-08-19)——抽出器の投入前 2,896枚、 投入後 2,928枚(生成が続いているフォルダなので件数は動きます。読むべきは割合で、 抽出前はすべて 0.0% でした)。
| 欄 | 抽出前 | 抽出後 |
|---|---|---|
model.name |
0.0% | 99.2% |
prompt.positive |
0.0% | 98.9% |
seed・steps・cfg・sampler・scheduler |
0.0% | 99.9% |
image.width |
0.0% | 99.9% |
adapters(LoRA) |
0.0% | 81.6% |
| 記録の充足度 | partial 2,894件 |
sufficient 2,841件 |
推測せず、拒否します。 グラフが一意に答えるときだけ埋め、埋めなかった欄には
機械可読の理由が必ず残ります(provenance.unreadKeys)——中核欄が欠けた55枚は全件が理由付きでした。
具体的には、チェックポイントのローダが2個あれば model を埋めず、独立したサンプラが2個あれば
sampling を埋めません。正負のプロンプトはサンプラの positive / negative リンクを辿って決めます——
ノードの並び順から推測すると、テキストノードが2個でない 4.3% で必ず取り違えます。
残る限界は2つです。
model.baseModelは画像からは原理的に埋まりません。 したがって系統に依存する機能 (CLIP Skip の無効判定・差し替えの安全域)は、画像経路では推測せず黙ります——assessAxisはunknown、checkSubstitutionはnullを返し、掃引は計画されます。 節約が効かないだけで、誤った断定はしません。 この振る舞いはこの段落より新しい。 2026-08-19 まで、系統が不明なときclip-skipはeffective(効く)と断定していました——理由欄に「系統が不明」と書きながらです。 結果、ファイルから読んだ SDXL 系の画像に対して、効かないと分かっている軸の掃引を勧めて いました。この機能が防ぐために存在する失敗の、そのものの裏返しです。 いまは全軸に対するテストで固定しています——理由が判断材料の不在を述べているとき、 判定が断定側になってはならない。- グラフから読めるのは「名前」だけです。 hash も配布元IDも書かれていないので、
許諾の照合は名前一致までに留まります。本資料のその他の実測値は、すべて
台帳由来の記録(hash と
baseModelを持つ)で測ったものです。
したがって最も効くのは、既にモデル台帳か生成ログを持つ組織です——資源が hash と 配布元IDを持つのはその経路だけで、許諾の照合が本当に必要とするのはそこです。 画像の直読みは台帳から落ちた資産の救出経路で、設定はほぼ取り戻せるようになりましたが、 取り戻せるのは名前までです。
| 処理 | 1万件 | 10万件 |
|---|---|---|
| 充足度・再現可否・許諾・来歴の4判定 合計 | 143 ms | 896 ms |
summarizeCorpus(全件を1回で集計) |
65 ms | 558 ms |
| 常駐メモリ | 167 MB | 812 MB |
10万件の全数判定が1秒台です。 費用は計算ではなく、カタログの整備にかかります。
CLI・常駐・画面・スケジューラ・保存は含みません。書くのは主に4つです。
- カタログ変換器 — 自社台帳 →
createEnvironment/許諾表 →createLicenseCatalog。ここが実際の導入費用です - 走査 — ディレクトリ/DAM/DBの反復。配列にできれば
summarizeCorpusが受けます - 保存 — 判定結果は計算するだけで保存しません
- 禁止規則 —
blocklistは利用者が書く関数です(「うちは触らない」は組織ごとに違うため)
createLicenseCatalog が受ける項目: hash / sourceId / versionId / name(照合鍵・いずれか)、
permissions(配列。null は「不明」、空配列は「何も許諾しない」で意味が違う)、
blocked / blockedReason / baseModel / title / sourceUrl。
createEnvironment が受ける項目: kind('model'/'adapter')/ name / hash / path。
scanModelDirectories でモデル置き場を走査すれば環境側は自動生成できますが、
走査で作った項目は hash を持たないので名前一致だけになります。
この但し書きは本節の全数値に掛かります。 御社では分布が変わります。
- 判定できないものを「可」へ寄せる経路が、コード上に存在しません。 集約は最も弱い構成に従い
(
blocked > forbidden > unknown > allowed)、カタログに無い資源はunknown、 構成が空ならunknown(「問題なし」ではない)。独立に作られた328件の分類との突き合わせで 危険側の誤答 0件。 これは母集団の性質ではなく手法の性質です - 記録が無い26件を、環境情報を一切見ずに検出。参照実装も同じ欠落を読んで全件
unavailableと 分類しました。1枚も生成していません——当てた率ではなく、 記録が無ければ復元できないという定義が働いた結果です - 来歴を説明できないものが4件。うち1件は商用利用が許諾されています—— ライセンスの確認は、来歴の説明にはなりません
- 再現できない46件のうち22件はプロンプトが残っており、派生の素材としては使えます
| 種別 | 例 | 何で動くか |
|---|---|---|
| 機構級 | 「不明を可へ寄せない」「記録に無いものは復元できない」「系統不明は null」 |
動きません。 コードを読めば確かめられる性質 |
| 母集団級 | 298/22/26・SDXL系291件・派生341件・3,509回 | 御社の資産で変わります |
| 追随級 | 解決98.9%・判定80.9%・偽の「できます」10.9%・再現到達率 94.0/90.9/0.0%・推測ゼロ32.7% | **参照実装が変われば動きます。**こちらの合否ではありません |
追随級は注意が要ります。 validate.mjs は「再現できた」を 2026-08-11 に凍結した
参照実装のラベルが unavailable でないことで数えており、生成の実測ではありません。
以前の版が「実際に再現到達率0.0%」と書いていた箇所は、正確には
「両方の実装が同じ欠落を読んで同じ結論に達した」です。
なお参照実装が実行可能と判定した300件のうち、67件は人間の目視で劣化が報告されています。
「実行できる」は「同じ絵が出る」ではありません。
「入手できるか」の判定は、宣言した合格条件(誤って「できる」と答える率 3%未満)に対し
10.9% で未達です(2026-08-11 時点の参照実装の出力との比較)。原因は構造的で、
入手可否を判断するための signal が記録側に存在しません。手法の限界であって、修正待ちの
不具合ではありません。 開示はしますが、解決の時期は約束しません。
運用での回避: not-present は「探す価値がある」までで、「入手できる」と読まないでください。
不足の理由の3分割は健全で、入手可否の判定だけが未達です。
| genrecord が答えること | 御社が答えること | |
|---|---|---|
| 許諾 | 表明された許諾をこの構成へ適用した結果と、阻んでいる構成 | 許諾の内容が正しく最新か。リスクを受け入れるか |
| 許諾データ | 何も持ちません(カタログを同梱しません) | カタログの用意と維持 |
| 来歴 | 記録が主張する内容と、沈黙している箇所 | 記録が真実か。真正性は検証していません |
| 再現 | 記録された資源が、渡された環境で解決するか | 出た絵が受け入れられるか |
| 入手可否 | 信頼できる答えを出しません(上記の未達) | 調達 |
| 法的判断 | 何も答えません。法的助言ではありません | 判断 |
| 運用 | ライブラリ呼び出し1つ | その周り全部(反復・保存・実行契機・画面・権限) |
ライセンスはデュアルです。自由側は AGPL-3.0、これを受け入れられない場合の商用 ライセンスを期間または永続の2形態で用意します。どちらも保守・更新・サポートを 含みません(永続形態は、それを作者に頼らずに済むようにするためのものです)。 Pull Request はまだ受け付けていません(CLA が未整備で、外部の著作物が入ると デュアルライセンスが成立しなくなるため)。
商用ライセンスのお問い合わせ: syugoji@gmail.com(日本語で構いません)。 条件の詳細は COMMERCIAL.md にあります。御社データでの評価が先で、 条件の話はその後です。不具合の報告と自由側の質問は Issue へお願いします。