Repository: anza-xyz/agave
Pull request #5317: Remove unnecessary svm-internal features from crates
Author: login: pgarg66
Created: 2025-03-16T18:16:39Z | Merged: 2025-03-19T00:39:57Z

#### Problem
The following crates are no longer internal to SVM, and should not use `svm-internal` feature to export or consume internal APIs.
1. `builtins-default-costs`
2. `compute-budget-instruction`
3. `programs/compute-budget`

#### Summary of Changes
Remove the feature and its usage from these crates.

Fixes #
<!-- OPTIONAL: Feature Gate Issue: # -->
<!-- Don't forget to add the "feature-gate" label -->



Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 593bc1ac4f1a6f..a32c53b26d5409 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6811,11 +6811,9 @@ dependencies = [
  "ahash 0.8.11",
  "lazy_static",
  "log",
- "qualifier_attr",
  "rand 0.8.5",
  "solana-address-lookup-table-program",
  "solana-bpf-loader-program",
- "solana-builtins-default-costs",
  "solana-compute-budget-program",
  "solana-config-program",
  "solana-feature-set",
@@ -7267,7 +7265,6 @@ dependencies = [
 name = "solana-compute-budget-program"
 version = "2.3.0"
 dependencies = [
- "qualifier_attr",
  "solana-program-runtime",
 ]
 
diff --git a/builtins-default-costs/Cargo.toml b/builtins-default-costs/Cargo.toml
index ec1ac7e015a6ed..415e22f4ff4c80 100644
--- a/builtins-default-costs/Cargo.toml
+++ b/builtins-default-costs/Cargo.toml
@@ -13,16 +13,15 @@ edition = { workspace = true }
 ahash = { workspace = true }
 lazy_static = { workspace = true }
 log = { workspace = true }
-qualifier_attr = { workspace = true }
 solana-address-lookup-table-program = { workspace = true }
-solana-bpf-loader-program = { workspace = true, features = ["svm-internal"] }
-solana-compute-budget-program = { workspace = true, features = ["svm-internal"] }
+solana-bpf-loader-program = { workspace = true }
+solana-compute-budget-program = { workspace = true }
 solana-config-program = { workspace = true }
 solana-feature-set = { workspace = true }
 solana-frozen-abi = { workspace = true, optional = true, features = [
     "frozen-abi",
 ] }
-solana-loader-v4-program = { workspace = true, features = ["svm-internal"] }
+solana-loader-v4-program = { workspace = true, features = ["agave-unstable-api"] }
 solana-pubkey = { workspace = true }
 solana-sdk-ids = { workspace = true }
 solana-stake-program = { workspace = true }
@@ -36,7 +35,6 @@ name = "solana_builtins_default_costs"
 
 [dev-dependencies]
 rand = "0.8.5"
-solana-builtins-default-costs = { path = ".", features = ["svm-internal"] }
 static_assertions = { workspace = true }
 
 [package.metadata.docs.rs]
@@ -48,7 +46,6 @@ frozen-abi = [
     "solana-vote-program/frozen-abi",
 ]
 dev-context-only-utils = []
-svm-internal = []
 
 [lints]
 workspace = true
diff --git a/builtins-default-costs/src/lib.rs b/builtins-default-costs/src/lib.rs
index 2653deb7a9cc72..04b2d2a8c820ca 100644
--- a/builtins-default-costs/src/lib.rs
+++ b/builtins-default-costs/src/lib.rs
@@ -1,7 +1,5 @@
 #![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
 #![allow(clippy::arithmetic_side_effects)]
-#[cfg(feature = "svm-internal")]
-use qualifier_attr::qualifiers;
 use {
     ahash::AHashMap,
     lazy_static::lazy_static,
@@ -15,8 +13,7 @@ use {
 };
 
 #[derive(Clone)]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-struct MigratingBuiltinCost {
+pub struct MigratingBuiltinCost {
     native_cost: u64,
     core_bpf_migration_feature: Pubkey,
     // encoding positional information explicitly for migration feature item,
@@ -27,8 +24,7 @@ struct MigratingBuiltinCost {
 }
 
 #[derive(Clone)]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-struct NotMigratingBuiltinCost {
+pub struct NotMigratingBuiltinCost {
     native_cost: u64,
 }
 
@@ -39,8 +35,7 @@ struct NotMigratingBuiltinCost {
 /// When migration completed, eg the feature gate is enabled everywhere, please
 /// remove that builtin entry from MIGRATING_BUILTINS_COSTS.
 #[derive(Clone)]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-enum BuiltinCost {
+pub enum BuiltinCost {
     Migrating(MigratingBuiltinCost),
     NotMigrating(NotMigratingBuiltinCost),
 }
@@ -53,7 +48,6 @@ impl BuiltinCost {
         }
     }
 
-    #[cfg(feature = "svm-internal")]
     fn core_bpf_migration_feature(&self) -> Option<&Pubkey> {
         match self {
             BuiltinCost::Migrating(MigratingBuiltinCost {
@@ -64,7 +58,6 @@ impl BuiltinCost {
         }
     }
 
-    #[cfg(feature = "svm-internal")]
     fn position(&self) -> Option<usize> {
         match self {
             BuiltinCost::Migrating(MigratingBuiltinCost { position, .. }) => Some(*position),
@@ -116,8 +109,7 @@ static_assertions::const_assert_eq!(
     TOTAL_COUNT_BUILTINS
 );
 
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-const MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
+pub const MIGRATING_BUILTINS_COSTS: &[(Pubkey, BuiltinCost)] = &[
     (
         stake::id(),
         BuiltinCost::Migrating(MigratingBuiltinCost {
@@ -223,17 +215,13 @@ pub fn get_builtin_instruction_cost<'a>(
         .map(|builtin_cost| builtin_cost.native_cost())
 }
 
-#[cfg(feature = "svm-internal")]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-enum BuiltinMigrationFeatureIndex {
+pub enum BuiltinMigrationFeatureIndex {
     NotBuiltin,
     BuiltinNoMigrationFeature,
     BuiltinWithMigrationFeature(usize),
 }
 
-#[cfg(feature = "svm-internal")]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-fn get_builtin_migration_feature_index(program_id: &Pubkey) -> BuiltinMigrationFeatureIndex {
+pub fn get_builtin_migration_feature_index(program_id: &Pubkey) -> BuiltinMigrationFeatureIndex {
     BUILTIN_INSTRUCTION_COSTS.get(program_id).map_or(
         BuiltinMigrationFeatureIndex::NotBuiltin,
         |builtin_cost| {
@@ -266,9 +254,7 @@ const _: () = validate_position(MIGRATING_BUILTINS_COSTS);
 
 /// Helper function to return ref of migration feature Pubkey at position `index`
 /// from MIGRATING_BUILTINS_COSTS
-#[cfg(feature = "svm-internal")]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-pub(crate) fn get_migration_feature_id(index: usize) -> &'static Pubkey {
+pub fn get_migration_feature_id(index: usize) -> &'static Pubkey {
     MIGRATING_BUILTINS_COSTS
         .get(index)
         .expect("valid index of MIGRATING_BUILTINS_COSTS")
diff --git a/compute-budget-instruction/Cargo.toml b/compute-budget-instruction/Cargo.toml
index 5a80dc3ba86bdd..a9d425b793ecf3 100644
--- a/compute-budget-instruction/Cargo.toml
+++ b/compute-budget-instruction/Cargo.toml
@@ -12,7 +12,7 @@ edition = { workspace = true }
 [dependencies]
 log = { workspace = true }
 solana-borsh = { workspace = true }
-solana-builtins-default-costs = { workspace = true, features = ["svm-internal"] }
+solana-builtins-default-costs = { workspace = true }
 solana-compute-budget = { workspace = true }
 solana-compute-budget-interface = { workspace = true, features = ["borsh"] }
 solana-feature-set = { workspace = true }
@@ -32,7 +32,7 @@ name = "solana_compute_budget_instruction"
 bincode = { workspace = true }
 criterion = { workspace = true }
 rand = { workspace = true }
-solana-builtins-default-costs = { workspace = true, features = ["dev-context-only-utils", "svm-internal"] }
+solana-builtins-default-costs = { workspace = true, features = ["dev-context-only-utils"] }
 solana-hash = { workspace = true }
 solana-keypair = { workspace = true }
 solana-message = { workspace = true }
diff --git a/programs/compute-budget/Cargo.toml b/programs/compute-budget/Cargo.toml
index a52f98b4506449..4a69c15a017a93 100644
--- a/programs/compute-budget/Cargo.toml
+++ b/programs/compute-budget/Cargo.toml
@@ -10,15 +10,11 @@ license = { workspace = true }
 edition = { workspace = true }
 
 [dependencies]
-qualifier_attr = { workspace = true }
 solana-program-runtime = { workspace = true }
 
 [lib]
 crate-type = ["lib"]
 name = "solana_compute_budget_program"
 
-[features]
-svm-internal = []
-
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/programs/compute-budget/src/lib.rs b/programs/compute-budget/src/lib.rs
index 271564c744e3a9..01bbd7a8b4f21c 100644
--- a/programs/compute-budget/src/lib.rs
+++ b/programs/compute-budget/src/lib.rs
@@ -1,9 +1,6 @@
-#[cfg(feature = "svm-internal")]
-use qualifier_attr::qualifiers;
 use solana_program_runtime::declare_process_instruction;
 
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-const DEFAULT_COMPUTE_UNITS: u64 = 150;
+pub const DEFAULT_COMPUTE_UNITS: u64 = 150;
 
 declare_process_instruction!(Entrypoint, DEFAULT_COMPUTE_UNITS, |_invoke_context| {
     // Do nothing, compute budget instructions handled by the runtime
diff --git a/programs/loader-v4/Cargo.toml b/programs/loader-v4/Cargo.toml
index 48e4b359a1cc16..8e1935e9bde57f 100644
--- a/programs/loader-v4/Cargo.toml
+++ b/programs/loader-v4/Cargo.toml
@@ -40,6 +40,7 @@ name = "solana_loader_v4_program"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [features]
+agave-unstable-api = []
 shuttle-test = [
     "solana-type-overrides/shuttle-test",
     "solana-program-runtime/shuttle-test",
diff --git a/programs/loader-v4/src/lib.rs b/programs/loader-v4/src/lib.rs
index 149afefd993459..611c6ed26f8633 100644
--- a/programs/loader-v4/src/lib.rs
+++ b/programs/loader-v4/src/lib.rs
@@ -1,4 +1,4 @@
-#[cfg(feature = "svm-internal")]
+#[cfg(feature = "agave-unstable-api")]
 use qualifier_attr::qualifiers;
 use {
     solana_bincode::limited_deserialize,
@@ -26,7 +26,7 @@ use {
     std::{cell::RefCell, rc::Rc},
 };
 
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
+#[cfg_attr(feature = "agave-unstable-api", qualifiers(pub))]
 const DEFAULT_COMPUTE_UNITS: u64 = 2_000;
 
 pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> {
diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock
index 5ea3f1db5982b8..b23939e23f9ee4 100644
--- a/programs/sbf/Cargo.lock
+++ b/programs/sbf/Cargo.lock
@@ -5522,7 +5522,6 @@ dependencies = [
  "ahash 0.8.11",
  "lazy_static",
  "log",
- "qualifier_attr",
  "solana-address-lookup-table-program",
  "solana-bpf-loader-program",
  "solana-compute-budget-program",
@@ -5761,7 +5760,6 @@ dependencies = [
 name = "solana-compute-budget-program"
 version = "2.3.0"
 dependencies = [
- "qualifier_attr",
  "solana-program-runtime",
 ]
 
diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock
index 6275306398a22b..4c821dbc52325a 100644
--- a/svm/examples/Cargo.lock
+++ b/svm/examples/Cargo.lock
@@ -5380,7 +5380,6 @@ dependencies = [
  "ahash 0.8.11",
  "lazy_static",
  "log",
- "qualifier_attr",
  "solana-address-lookup-table-program",
  "solana-bpf-loader-program",
  "solana-compute-budget-program",
@@ -5619,7 +5618,6 @@ dependencies = [
 name = "solana-compute-budget-program"
 version = "2.3.0"
 dependencies = [
- "qualifier_attr",
  "solana-program-runtime",
 ]
 
diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs
index adf2fec8699ce5..337642cd42bc2c 100644
--- a/svm/src/transaction_processor.rs
+++ b/svm/src/transaction_processor.rs
@@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 1000u64;
         let last_blockhash = Hash::new_unique();
         let message = new_unchecked_sanitized_message(Message::new_with_blockhash(
             &[


Reviews:
user: login: tao-stones
body: Not up-to-date with svm changes, but if it's easy to identify what changed so "these crates are no longer internal to SVM", that'd be helpful to review. My initial thought is most of these can stay private. 
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-17T21:06:59Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-17T21:27:55Z
user: login: t-nelson
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-17T22:40:51Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-17T23:05:49Z
user: login: tao-stones
state: DISMISSED
author_association: NONE
submitted_at: 2025-03-18T03:21:08Z
user: login: Lichtso
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T16:41:29Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T17:54:28Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T17:57:36Z
user: login: Lichtso
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T21:38:43Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T23:01:05Z
user: login: pgarg66
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-18T23:06:40Z
user: login: Lichtso
state: APPROVED
author_association: NONE
submitted_at: 2025-03-18T23:29:32Z
user: login: tao-stones
state: APPROVED
author_association: NONE
submitted_at: 2025-03-19T00:38:19Z

Review comments:
diff_hunk: @@ -15,8 +13,7 @@ use {
 };
 
 #[derive(Clone)]
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-struct MigratingBuiltinCost {
+pub struct MigratingBuiltinCost {
path: builtins-default-costs/src/lib.rs
user: login: tao-stones
body: Is it necessary to make this, and the rest of changes, `pub`? They could remain private. 
created_at: 2025-03-17T21:02:36Z
author_association: NONE
diff_hunk: @@ -1,9 +1,6 @@
-#[cfg(feature = "svm-internal")]
-use qualifier_attr::qualifiers;
 use solana_program_runtime::declare_process_instruction;
 
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-const DEFAULT_COMPUTE_UNITS: u64 = 150;
+pub const DEFAULT_COMPUTE_UNITS: u64 = 150;
path: programs/compute-budget/src/lib.rs
user: login: tao-stones
body: this doesn't need to be 'pub', does it?
created_at: 2025-03-17T21:03:58Z
author_association: NONE
diff_hunk: @@ -1,9 +1,6 @@
-#[cfg(feature = "svm-internal")]
-use qualifier_attr::qualifiers;
 use solana_program_runtime::declare_process_instruction;
 
-#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
-const DEFAULT_COMPUTE_UNITS: u64 = 150;
+pub const DEFAULT_COMPUTE_UNITS: u64 = 150;
path: programs/compute-budget/src/lib.rs
user: login: pgarg66
body: It's being used by `builtins-default-costs.rs`.
```
        compute_budget::id(),
        BuiltinCost::NotMigrating(NotMigratingBuiltinCost {
            native_cost: solana_compute_budget_program::DEFAULT_COMPUTE_UNITS,
        }),
```
created_at: 2025-03-17T21:27:54Z
author_association: NONE
diff_hunk: @@ -40,6 +40,7 @@ name = "solana_loader_v4_program"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [features]
+agave-internal = []
path: programs/loader-v4/Cargo.toml
user: login: t-nelson
body: i would prefer that the agave feature flag be more severe in it's naming

my vote would be for `agave-unstable-api`
created_at: 2025-03-17T22:40:51Z
author_association: NONE
diff_hunk: @@ -40,6 +40,7 @@ name = "solana_loader_v4_program"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [features]
+agave-internal = []
path: programs/loader-v4/Cargo.toml
user: login: pgarg66
body: Yeah, that's very reasonable. I'll update the PR.
created_at: 2025-03-17T23:05:49Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: Lichtso
body: Why this change? Aren't the numeric values different 150 vs 2000?
created_at: 2025-03-18T16:41:29Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: pgarg66
body: The test doesn't seem to care about the actual value.
created_at: 2025-03-18T17:54:28Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: pgarg66
body: The change is also to remove dep on `programs/compute-budget` crate.
Earlier it was accessible because of this:
```
#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
const DEFAULT_COMPUTE_UNITS: u64 = 150;
```
created_at: 2025-03-18T17:57:36Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: Lichtso
body: If the value is irrelevant, why use the named constant instead of a literal?
created_at: 2025-03-18T21:38:43Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: pgarg66
body: Yeah, good question!
Not sure if it needs a specific lower bound for the value. I'll try.
created_at: 2025-03-18T23:01:05Z
author_association: NONE
diff_hunk: @@ -2498,7 +2498,7 @@ mod tests {
     fn test_validate_transaction_fee_payer_is_nonce() {
         let lamports_per_signature = 5000;
         let rent_collector = RentCollector::default();
-        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
+        let compute_unit_limit = 2 * solana_loader_v4_program::DEFAULT_COMPUTE_UNITS;
path: svm/src/transaction_processor.rs
user: login: pgarg66
body: The test seems to use this value for the priority fee. So any value would have worked. I've removed the use of the constant.
created_at: 2025-03-18T23:06:40Z
author_association: NONE

PR comments:
user: login: mergify[bot]
created_at: 2025-03-16T18:17:14Z
body: The Firedancer team maintains a line-for-line reimplementation of the
native programs, and until native programs are moved to BPF, those
implementations must exactly match their Agave counterparts.
If this PR represents a change to a native program implementation (not
tests), please include a reviewer from the Firedancer team. And please
keep refactors to a minimum.
author_association: NONE
user: login: LucasSte
created_at: 2025-03-17T10:28:15Z
body: The PR looks good. I just have one question for better understanding. These crates won't be internal to SVM anymore, but will they be part of the SVM repo?
author_association: NONE
user: login: pgarg66
created_at: 2025-03-17T13:03:30Z
body: > The PR looks good. I just have one question for better understanding. These crates won't be internal to SVM anymore, but will they be part of the SVM repo?

No, these crates won't be in SVM repo 
author_association: NONE
user: login: apfitzge
created_at: 2025-03-17T14:36:03Z
body: requested a review from @tao-stones, as he did most of the builtin cost implementation. He may have a better opinion on whether or not it's okay for some of these structs/consts to become unconditionally public.
author_association: NONE
user: login: pgarg66
created_at: 2025-03-17T21:35:22Z
body: > Not up-to-date with svm changes, but if it's easy to identify what changed so "these crates are no longer internal to SVM", that'd be helpful to review. My initial thought is most of these can stay private.

Actually, the visibility of the structs and variables is back to its original state before `svm-internal` was introduced. The only reason we were able to restrict the visibility was because these different crates were using the feature to gain the access.

I think if we want to restrict the access, a different feature should be shared between these crates.
author_association: NONE
user: login: tao-stones
created_at: 2025-03-18T03:20:53Z
body: > > Not up-to-date with svm changes, but if it's easy to identify what changed so "these crates are no longer internal to SVM", that'd be helpful to review. My initial thought is most of these can stay private.
> 
> Actually, the visibility of the structs and variables is back to its original state before `svm-internal` was introduced. The only reason we were able to restrict the visibility was because these different crates were using the feature to gain the access.
> 
> I think if we want to restrict the access, a different feature should be shared between these crates.

Ah, thanks for explaining. Agree it is a good idea to use dedicated feature if further restriction is needed. Ok with restoring to original visibility for now. 
author_association: NONE