Skip to content

compliance: release the weights under Apache-2.0 - #12

Merged
usik-luke-trl merged 2 commits into
mainfrom
compliance/model-license-apache
Oct 14, 2026
Merged

usik-luke-trl merged 2 commits into
mainfrom
compliance/model-license-apache

Conversation

@usik-luke-trl

Copy link
Copy Markdown
Contributor

Draft — blocked on the Hugging Face model card. Replaces #6, which GitHub auto-closed when its base branch (supply-chain/ci) was deleted on merge of #5. Same content, rebuilt on main.

What this says

COMPLIANCE.md §4b records trillionlabs/Trida2.0-4B as Apache-2.0. The inputs permit it:

input status
base model Qwen/Qwen3.5-4B Apache-2.0, verified against HF metadata
training data Trillion Labs' own, internal

What Apache-2.0 on the weights does not mean

path kernels terms for the operator
--mode causal (AR) stock vLLM, no §1b dependency Apache-2.0 throughout
--mode diffusion / self-spec §1b two-stream kernel PolyForm Noncommercial

The restriction attaches to running that kernel, not to holding the weights. The scope is one file — block_causal_readout.py — not the diffusion path as a whole; everything around it (qwen3_5_diffusion.py, ~2,000 lines, the plugin, the decode algorithms) is first-party Apache-2.0.

Reasoning for why the kernel doesn't encumber the weights is recorded in §4b: the weights aren't a derivative work of the kernel source, so the licence governs use of the software rather than ownership of its output. Engineering and compliance reading, explicitly not a legal opinion — the scope of "noncommercial purpose" is the point for counsel before commercial distribution.

Why CI is red, and why that's correct

tools/check_model_card.py compares §4b against HF's live metadata. The repo now claims Apache-2.0; the card still says other:

licence mismatch: Hugging Face reports 'other',
COMPLIANCE.md section 4b says 'Apache-2.0'

This cannot merge until the model card is updated, so the repo can never claim a licence the model doesn't publish. Verified both directions.

It exists because these claims already drifted once — the model went public on 2026-09-19 and five files kept telling users to authenticate for two weeks. It runs weekly alongside the fetch-recipe check.

To unblock — the model card change

On the HF repo. Frontmatter:

---
license: apache-2.0
base_model: Qwen/Qwen3.5-4B
tags:
  - block-diffusion
  - hybrid-diffusion
  - self-speculative-decoding
  - qwen3.5
---

Plus a ## License section in the body, since license: apache-2.0 alone would imply the diffusion path is permissive:

Model weights are Apache-2.0. The base model (Qwen/Qwen3.5-4B) is Apache-2.0, and no use restriction propagates from it.

The decode kernels are licensed separately from the weights. The block-diffusion and self-speculative serving paths require a two-stream Gated-DeltaNet kernel that is PolyForm Noncommercial 1.0.0, fetched from upstream rather than bundled — see COMPLIANCE.md §1b. Running those paths is noncommercial for the operator. Autoregressive decoding (--mode causal) has no such dependency.

README_ko.md needs the same section or the two cards will disagree; Korean draft available on request.

Note: if the planned kernel reimplementation lands first, the PolyForm paragraphs above come out and this PR should be revised before merge — the kernel wording would then be wrong in the other direction.

Also here

The model and its base are added to asset-register.csv — the guideline's 통합자산목록 covers models and datasets, not just software.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs

usik-luke-trl added a commit that referenced this pull request Oct 6, 2026
Turns the channel decision into an executable plan. Discord is settled -- public
invitation is the default, and the guideline warns against Slack's free tier as a
permanent knowledge store.

The structure that matters is the launch-hold section. Three conditions must hold
before public invitations open, and the first is unverified: nobody has confirmed
that security@ and conduct@ actually deliver, to two people each. A code of conduct
whose report address is dead is not operational, and community-check.yml can only
test that an address is stated.

Phases: merge #14 and assign roles -> build the server -> wire notifications ->
seed content -> limited release. Phase 3 is the largest, because FAQs and
first-issue candidates have to be real ones rather than filler.

Two platform constraints are written down rather than discovered later: GitHub
repository webhooks subscribe by event type and cannot filter on labels, so
"help wanted" needs an Actions workflow; and replying from Discord into GitHub is
not practically possible, which is why promotion is a documented duty.

Promotion is also what keeps M-001 honest -- it reads response time from GitHub
comment timestamps, so an answer given only in chat makes the metric wrong and
loses the knowledge. stale-unanswered.yml is the backstop: such an item keeps
reappearing every weekday until someone records it.

12 acceptance criteria, each checkable. Notably #12 requires one real promotion to
have happened, because a rule nobody has exercised is not yet a practice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
usik-luke-trl added a commit that referenced this pull request Oct 6, 2026
Turns the channel decision into an executable plan. Discord is settled -- public
invitation is the default, and the guideline warns against Slack's free tier as a
permanent knowledge store.

The structure that matters is the launch-hold section. Three conditions must hold
before public invitations open, and the first is unverified: nobody has confirmed
that security@ and conduct@ actually deliver, to two people each. A code of conduct
whose report address is dead is not operational, and community-check.yml can only
test that an address is stated.

Phases: merge #14 and assign roles -> build the server -> wire notifications ->
seed content -> limited release. Phase 3 is the largest, because FAQs and
first-issue candidates have to be real ones rather than filler.

Two platform constraints are written down rather than discovered later: GitHub
repository webhooks subscribe by event type and cannot filter on labels, so
"help wanted" needs an Actions workflow; and replying from Discord into GitHub is
not practically possible, which is why promotion is a documented duty.

Promotion is also what keeps M-001 honest -- it reads response time from GitHub
comment timestamps, so an answer given only in chat makes the metric wrong and
loses the knowledge. stale-unanswered.yml is the backstop: such an item keeps
reappearing every weekday until someone records it.

12 acceptance criteria, each checkable. Notably #12 requires one real promotion to
have happened, because a rule nobody has exercised is not yet a practice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
usik-luke-trl added a commit that referenced this pull request Oct 6, 2026
Turns the channel decision into an executable plan. Discord is settled -- public
invitation is the default, and the guideline warns against Slack's free tier as a
permanent knowledge store.

The structure that matters is the launch-hold section. Three conditions must hold
before public invitations open, and the first is unverified: nobody has confirmed
that security@ and conduct@ actually deliver, to two people each. A code of conduct
whose report address is dead is not operational, and community-check.yml can only
test that an address is stated.

Phases: merge #14 and assign roles -> build the server -> wire notifications ->
seed content -> limited release. Phase 3 is the largest, because FAQs and
first-issue candidates have to be real ones rather than filler.

Two platform constraints are written down rather than discovered later: GitHub
repository webhooks subscribe by event type and cannot filter on labels, so
"help wanted" needs an Actions workflow; and replying from Discord into GitHub is
not practically possible, which is why promotion is a documented duty.

Promotion is also what keeps M-001 honest -- it reads response time from GitHub
comment timestamps, so an answer given only in chat makes the metric wrong and
loses the knowledge. stale-unanswered.yml is the backstop: such an item keeps
reappearing every weekday until someone records it.

12 acceptance criteria, each checkable. Notably #12 requires one real promotion to
have happened, because a rule nobody has exercised is not yet a practice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
usik-luke-trl added a commit that referenced this pull request Oct 6, 2026
…wered alert (#14)

* community: channel map, chat policy, moderator runbook, and the unanswered alert

Prepares the chat channel without opening one. The guideline's first instruction is
"첫 주에는 서버부터 만들지 않는다" -- purpose, channel map, roles and response
expectations are settled before any server exists, and this is that work.

Public-facing
- COMMUNITY.md: where to ask what, and what to expect back. The chat row is left
  empty on purpose rather than carrying a placeholder link.
- README gains a help/contribute/report section. The repository checklist is judged
  from a logged-out browser, and until now the README linked to neither
  CONTRIBUTING.md nor SECURITY.md -- the first checklist item, failing.

Operations (docs/community/)
- CHANNEL_MATRIX: which system each kind of enquiry COMPLETES in, the order to decide
  by, what a transition comment must carry, and alarm design.
- CHANNEL_POLICY: minimum channel structure, roles with least privilege, enforcement.
- MODERATOR_RUNBOOK: intake, S1-S4, incident records, the 8-step promotion procedure,
  and the four drills to run before opening.
- LABELS: what each label means AND when it comes off, which the checklist requires.

Automation
- community-check.yml: required files present, and the reporting routes still stated.
  It fails today's README, which is how the gap above was found rather than assumed.
- stale-unanswered.yml + tools/notify_stale.py: weekday mornings, items with no human
  response for 48h. Of everything the guideline says is worth sending to chat, this is
  the one that cannot be a GitHub notification -- it is the absence of an event.
  Dry run found a real case: PR #4 open 138 hours, unanswered.
- community_to_issue.yml: the chat-to-issue form, so promotion is a form rather than
  something remembered.

Discord or Slack is deliberately not decided here. Only the webhook differs, and
notify_stale posts to whichever secret is set; with neither it prints and does not fail.

Why promotion is written into the runbook rather than left as etiquette: M-001
measures response from GitHub comment timestamps. An answer given only in chat makes
the metric silently wrong AND loses the knowledge, which is the named risk of chat
channels -- 지식 소실, 검색·보존 제한. The 48h alert backstops it: an item answered
only in chat keeps reappearing until someone records it.

Also enabled Discussions and added the triage/community labels.

Not done, and listed as launch-hold conditions in docs/community/README.md: nobody
has verified that security@ and conduct@ actually deliver to two people each. CI can
check an address is stated; it cannot check mail arrives. The guideline says do not
open public invitations until that is confirmed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs

* docs(community): Discord launch plan

Turns the channel decision into an executable plan. Discord is settled -- public
invitation is the default, and the guideline warns against Slack's free tier as a
permanent knowledge store.

The structure that matters is the launch-hold section. Three conditions must hold
before public invitations open, and the first is unverified: nobody has confirmed
that security@ and conduct@ actually deliver, to two people each. A code of conduct
whose report address is dead is not operational, and community-check.yml can only
test that an address is stated.

Phases: merge #14 and assign roles -> build the server -> wire notifications ->
seed content -> limited release. Phase 3 is the largest, because FAQs and
first-issue candidates have to be real ones rather than filler.

Two platform constraints are written down rather than discovered later: GitHub
repository webhooks subscribe by event type and cannot filter on labels, so
"help wanted" needs an Actions workflow; and replying from Discord into GitHub is
not practically possible, which is why promotion is a documented duty.

Promotion is also what keeps M-001 honest -- it reads response time from GitHub
comment timestamps, so an answer given only in chat makes the metric wrong and
loses the knowledge. stale-unanswered.yml is the backstop: such an item keeps
reappearing every weekday until someone records it.

12 acceptance criteria, each checkable. Notably #12 requires one real promotion to
have happened, because a rule nobody has exercised is not yet a practice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs

* docs(metrics): keep the definitions, drop the collection

#13 proposed collecting metrics in this repository -- a collector, tests, a monthly
workflow committing snapshots to main, and a baseline. That was wrong on three
counts. The team said they would handle collection themselves; a scheduled workflow
with contents: write pushing to the default branch is a real permission on a public
repo for data no outside reader consumes; and the current snapshot is mostly no_data
because there are no external contributors yet.

What stays is one page. It exists because three community documents cite it, and
because someone filing an issue should be able to see how their question is measured
and that they are not the thing being measured.

Definitions, the external-contributor scope and why it matters, why response time
and no-response rate are read together, the observation period with no targets yet,
why answering only in chat makes the numbers wrong, the explicit no-individual-
measurement stance, and the limitations.

No numbers, no collector, no workflow. Collection and reporting happen internally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
usik-luke-trl and others added 2 commits October 6, 2026 13:19
…l card)

Prepared ahead of the Hugging Face change. CI on this branch is RED on purpose and
goes green when the model card is updated -- see below.

COMPLIANCE.md 4b now records Trida2.0-4B as Apache-2.0. The inputs permit it: the
base model Qwen3.5-4B is Apache-2.0 (verified against HF metadata), the training
data is Trillion Labs' own, and nothing restrictive propagates from either.

It also states what Apache-2.0 on the weights does NOT mean. The diffusion and
self-speculative decode paths run the 1b kernels, which are PolyForm Noncommercial;
that restriction attaches to whoever runs the kernels, not to whoever holds the
weights. The AR path has no such dependency. A small table makes the split explicit
so nobody reads "Apache-2.0 weights" as "permissive serving stack".

On why the kernels do not encumber the weights: the weights are not a derivative
work of the kernel source -- they derive from the base model and the training data --
so the licence governs use of the software rather than ownership of its output, as a
compiler's licence does not reach the binaries it emits. Recorded as an engineering
and compliance reading, explicitly not a legal opinion; the scope of "noncommercial
purpose" is the point to put in front of a lawyer before commercial distribution.

New guard: tools/check_model_card.py compares COMPLIANCE.md 4b against Hugging
Face's live metadata -- declared licence and visibility -- and runs weekly. This
exists because those claims already drifted once: the model went public on
2026-09-19 and five files went on telling users to authenticate for two weeks,
because nothing was watching. Verified both ways: it passed against the current
card, and fails now that 4b claims Apache-2.0 while the card still says `other`.

That failure is the point. This branch cannot merge until the model card is
actually updated, so the repository can never claim a licence the model does not
publish.

Also adds the model and its base to the asset register -- the supply-chain
guideline's 통합자산목록 covers models and datasets, not only software, and mine
had only software.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
I claimed repeatedly that this branch could not merge while the Hugging Face card
still says `license: other`. That was wrong. The check was scoped to
`schedule || workflow_dispatch`, so it skipped on pull requests -- #12 showed
MERGEABLE/CLEAN, and taking it out of draft would have merged a repository claiming
Apache-2.0 against a card that publishes no terms at all.

Moved into its own workflow triggered by `pull_request` on the paths that carry the
claim: COMPLIANCE.md, the checker, and the workflow itself. A PR that edits
COMPLIANCE.md is a PR that changes what the repository asserts about the published
model, which is exactly when the assertion should be tested.

Deliberately not on every PR. While the card and the repo disagree this check fails,
and a red check on unrelated PRs is how people learn to ignore red checks -- the same
reasoning that moved pip-audit behind a policy file and renamed the paused Dependabot
config after it started failing a check on everything.

The weekly run stays, because claims drift with nobody editing a file. That is not
hypothetical here: the model went public on 2026-09-19 and five files went on telling
users to authenticate for two weeks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K599fUWZZYdiL4ekUq4Vhs
@usik-luke-trl
usik-luke-trl force-pushed the compliance/model-license-apache branch from 210beea to 70fb250 Compare October 6, 2026 04:19
@usik-luke-trl
usik-luke-trl marked this pull request as ready for review October 14, 2026 03:21
@usik-luke-trl
usik-luke-trl merged commit 8bc7020 into main Oct 14, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant