Implement Book of Owners and license system
- Add ownership ledger with public aliases and private identity mapping - Create license transaction tracking and upgrade automation - Implement validation system for SC/SK awards based on licenses - Set up privacy protection with .gitignore for sensitive data - Initialize founding entrepreneur as owner #001 - Ready for founding work audit processmaster
parent
0b777d311f
commit
5738af29df
|
@ -0,0 +1,7 @@
|
|||
# Privacy Protection - Never commit personal data
|
||||
ownership/identity-mapping.csv
|
||||
**/identity-mapping.csv
|
||||
|
||||
# Backup files
|
||||
*.backup
|
||||
*.bak
|
|
@ -0,0 +1,4 @@
|
|||
timestamp,type,amount,from,to,reference,description,approver,phase,evidence_link
|
||||
# Smartup Credits (SC) Transaction Ledger
|
||||
# Format: ISO timestamp, SC/REDEEM, amount, from_user, to_user, task_id, description, approver, phase, link_to_work
|
||||
# Example: 2025-01-15T10:30:00Z,SC,50,,alice,task-123,Backend API completion,bob,validation,https://forgejo.../issues/123
|
|
|
@ -0,0 +1,4 @@
|
|||
timestamp,type,amount,from,to,reference,description,awarded_by,category,evidence_link
|
||||
# Social Karma (SK) Transaction Ledger
|
||||
# Format: ISO timestamp, KARMA/DECAY, amount, from_user, to_user, event_id, description, awarder, category, evidence
|
||||
# Example: 2025-01-15T11:00:00Z,KARMA,10,,alice,pr-456,Excellent code review for mesh protocol,bob,code_review,https://forgejo.../pull/456
|
|
|
@ -0,0 +1,5 @@
|
|||
timestamp,event_type,amount_eur_delta,total_eur_balance,sc_outstanding,sc_liability_eur,ratio,notes,reference
|
||||
# Treasury Balance and SC Liability Tracking
|
||||
# Format: ISO timestamp, event_type, EUR_change, total_EUR, total_SC, SC_value_EUR, ratio, notes, reference
|
||||
# The 3x Rule: sc_liability_eur must be ≤ 3x total_eur_balance
|
||||
2025-01-15T00:00:00Z,INIT,0,0,0,0,0.0,Treasury initialized - Smartup Zero launch,system-init
|
|
|
@ -0,0 +1,2 @@
|
|||
owner_id,display_name,license_type,license_date,current_sk,voting_weight,status,last_updated
|
||||
001,robbert_founder,work,2025-08-04T14:37:16.470130Z,0,1.00,active,2025-08-04T14:37:16.470130Z
|
|
|
@ -0,0 +1,2 @@
|
|||
timestamp,owner_id,transaction_type,from_license,to_license,payment_eur,upgrade_reason,reference,approver
|
||||
2025-08-04T14:37:16.470130Z,001,FOUNDER,,work,0,founding_entrepreneur,system-init,smartup-zero-launch
|
|
|
@ -0,0 +1,45 @@
|
|||
# Smartup Credits (SC) Rate Structure
|
||||
# 1 SC = 1 EUR claim on treasury
|
||||
# Updated: 2025-01-15
|
||||
|
||||
# Standard Task Rates
|
||||
task_rates:
|
||||
small_task: 25 # 1-2 hours work
|
||||
medium_task: 50 # 3-4 hours work
|
||||
large_task: 100 # 5-8 hours work
|
||||
complex_task: 200 # 1-2 days work
|
||||
|
||||
# ADM Triangle Split (Attacker/Defender Model)
|
||||
adm_split:
|
||||
attacker_percentage: 90 # Senior contributor doing main work
|
||||
defender_percentage: 10 # Junior contributor learning + reviewing
|
||||
midfielder_percentage: 0 # Engelbot handles automatically
|
||||
|
||||
# Founding Work Audit Rates (Retroactive)
|
||||
founding_work_rates:
|
||||
research_hour: 25 # Historical R&D and investigation
|
||||
planning_hour: 25 # Business plan and strategy creation
|
||||
coding_hour: 30 # Technical development and prototyping
|
||||
writing_hour: 20 # Documentation and content creation
|
||||
governance_hour: 25 # Smartup model design and legal setup
|
||||
|
||||
# Special Contribution Rates
|
||||
special_rates:
|
||||
team_captain_monthly: 100 # Monthly leadership bonus
|
||||
mission_leader_bonus: 50 # Per completed objective
|
||||
emergency_fix: 150 # Critical bug fixes
|
||||
community_building: 75 # Outreach and recruitment
|
||||
|
||||
# Phase-Based Multipliers
|
||||
phase_multipliers:
|
||||
validation: 1.0 # Standard rates during proof phase
|
||||
design: 1.1 # 10% bonus during intensive design
|
||||
production: 1.2 # 20% bonus during crunch time
|
||||
organization: 1.0 # Back to standard for stable operations
|
||||
|
||||
# Treasury Protection Rules
|
||||
treasury_rules:
|
||||
max_outstanding_multiplier: 3 # Outstanding SC ≤ 3x cash treasury
|
||||
founding_work_cap: 10000 # Maximum retroactive SC for entrepreneur
|
||||
daily_mint_limit: 500 # Max SC mintable per person per day
|
||||
requires_defender_approval: true
|
|
@ -0,0 +1,70 @@
|
|||
# Social Karma (SK) Award Structure
|
||||
# Non-transferable reputation currency
|
||||
# Updated: 2025-01-15
|
||||
|
||||
# SK Award Categories
|
||||
karma_awards:
|
||||
# Code & Technical Quality
|
||||
code_review: 5 # Reviewing PRs and providing feedback
|
||||
bug_find: 10 # Finding and reporting bugs
|
||||
documentation: 15 # Writing guides and technical docs
|
||||
|
||||
# Community Building
|
||||
mentoring_session: 10 # One-on-one help with newcomers
|
||||
mentoring_weekly: 5 # Ongoing mentorship (awarded weekly)
|
||||
conflict_resolution: 25 # Successfully mediating disputes
|
||||
onboarding_help: 8 # Helping new members navigate
|
||||
|
||||
# Governance & Leadership
|
||||
governance_participation: 20 # Leading discussions in General Forum
|
||||
proposal_accepted: 20 # Successful proposals that pass votes
|
||||
meeting_facilitation: 15 # Running team meetings effectively
|
||||
cross_team_coordination: 12 # Bridging different teams
|
||||
|
||||
# Mission Completion
|
||||
objective_completion: 15 # Completing mission objectives
|
||||
deadline_hit: 10 # Meeting critical deadlines
|
||||
quality_delivery: 12 # Exceptional work quality recognition
|
||||
|
||||
# SK Privilege Thresholds
|
||||
privilege_thresholds:
|
||||
propose_in_forum: 50 # Can create binding vote proposals
|
||||
apply_team_captain: 100 # Eligible for team leadership roles
|
||||
lead_missions: 200 # Can be assigned as Mission Leader
|
||||
join_leadership_team: 500 # Eligible for Leadership Team
|
||||
|
||||
# Voting Weight (SK enhances but doesn't replace democracy)
|
||||
voting_weight:
|
||||
base_vote: 1.0 # Everyone gets 1 vote regardless
|
||||
sk_bonus_max: 1.5 # Maximum vote weight with high SK
|
||||
sk_bonus_threshold: 500 # SK needed for max voting bonus
|
||||
calculation: "min(1.5, 1 + (sk_points / 1000))"
|
||||
|
||||
# SK Decay Mechanism (prevents founder entrenchment)
|
||||
decay_rules:
|
||||
monthly_decay_rate: 0.10 # 10% monthly decay
|
||||
minimum_floor: 0 # Can decay to zero (stay active!)
|
||||
grace_period_days: 30 # No decay for first month after earning
|
||||
activity_bonus: 5 # Monthly bonus for active participation
|
||||
|
||||
# Anti-Gaming Measures
|
||||
validation_rules:
|
||||
cannot_buy: true # No amount of money gets you SK
|
||||
cannot_transfer: true # Your reputation is yours alone
|
||||
cannot_hoard: true # Use influence or lose it to decay
|
||||
peer_review_required: true # SK awards need defender confirmation
|
||||
|
||||
# SK Sources (who can award SK)
|
||||
award_sources:
|
||||
peer_recognition: true # Team members award each other
|
||||
captain_awards: true # Team Captains can award their team
|
||||
leadership_awards: true # Leadership Team for exceptional cases
|
||||
community_votes: true # General Forum for major contributions
|
||||
automated_systems: false # No bot-awarded SK (human judgment only)
|
||||
|
||||
# Monthly SK Budget (prevents inflation)
|
||||
team_budgets:
|
||||
per_team_monthly: 200 # Each team can award 200 SK/month
|
||||
leadership_monthly: 500 # Leadership Team additional budget
|
||||
community_monthly: 300 # General Forum discretionary awards
|
||||
rollover_limit: 0.5 # Unused budget 50% carries to next month
|
|
@ -0,0 +1,105 @@
|
|||
# License System Policies
|
||||
# Updated: 2025-01-15
|
||||
|
||||
# License Types and Capabilities
|
||||
license_capabilities:
|
||||
campaign:
|
||||
can_earn_sk: true
|
||||
can_earn_sc: false
|
||||
sk_categories: ["community_building", "governance_participation", "mentoring_help"]
|
||||
max_daily_sk: 25
|
||||
voting_rights: true
|
||||
forum_access: "1_general_forum"
|
||||
|
||||
watch:
|
||||
can_earn_sk: true
|
||||
can_earn_sc: false
|
||||
sk_categories: ["all_except_leadership"]
|
||||
max_daily_sk: 50
|
||||
voting_rights: true
|
||||
forum_access: "1_general_forum,limited_2_workplace"
|
||||
|
||||
work:
|
||||
can_earn_sk: true
|
||||
can_earn_sc: true
|
||||
sk_categories: ["all"]
|
||||
max_daily_sk: 100
|
||||
max_daily_sc: 500
|
||||
voting_rights: true
|
||||
forum_access: "all_tiers"
|
||||
|
||||
organizational:
|
||||
can_earn_sk: true
|
||||
can_earn_sc: true
|
||||
sk_categories: ["all"]
|
||||
max_daily_sk: 100
|
||||
max_daily_sc: 500
|
||||
voting_rights: true
|
||||
voting_weight_bonus: 0 # Same as individual, no special treatment
|
||||
forum_access: "all_tiers"
|
||||
|
||||
# Automatic Upgrade Rules
|
||||
upgrade_thresholds:
|
||||
campaign_to_watch:
|
||||
sk_required: 100
|
||||
additional_requirements: []
|
||||
approval_needed: false
|
||||
notification_required: true
|
||||
|
||||
watch_to_work:
|
||||
payment_required: 200 # EUR
|
||||
sk_minimum: 50 # Must demonstrate some engagement
|
||||
approval_needed: false
|
||||
|
||||
work_to_organizational:
|
||||
payment_required: 5000 # EUR
|
||||
sk_minimum: 200 # Must be proven contributor
|
||||
approval_needed: true # Community/Leadership review
|
||||
|
||||
# License Pricing (increases by phase)
|
||||
pricing:
|
||||
validation_phase:
|
||||
campaign: 0
|
||||
watch: 100
|
||||
work: 200
|
||||
organizational: 5000
|
||||
|
||||
design_phase:
|
||||
campaign: 0
|
||||
watch: 200
|
||||
work: 400
|
||||
organizational: 10000
|
||||
|
||||
production_phase:
|
||||
campaign: 0
|
||||
watch: 400
|
||||
work: 800
|
||||
organizational: 20000
|
||||
|
||||
organization_phase:
|
||||
campaign: 0
|
||||
watch: 800
|
||||
work: 1600
|
||||
organizational: 40000
|
||||
|
||||
# Identity and Privacy
|
||||
identity_rules:
|
||||
alias_required: false
|
||||
real_name_public: false
|
||||
can_change_alias: true
|
||||
alias_change_limit: 2 # per year
|
||||
privacy_levels: ["full_public", "alias_only", "anonymous"]
|
||||
|
||||
# Voting Weight Calculation
|
||||
voting_weight:
|
||||
base_weight: 1.0
|
||||
sk_bonus_formula: "min(1.5, 1 + (current_sk / 1000))"
|
||||
max_weight: 1.5
|
||||
organizational_bonus: 0 # No special voting power for big payments
|
||||
|
||||
# Status Management
|
||||
status_rules:
|
||||
inactive_threshold_days: 90 # No activity = inactive status
|
||||
suspension_requires: "leadership_vote"
|
||||
reactivation_requires: "login_activity"
|
||||
permanent_ban_requires: "community_vote"
|
|
@ -0,0 +1,82 @@
|
|||
# Currency Ledger Validation Rules
|
||||
# Ensures system integrity and democratic oversight
|
||||
# Updated: 2025-01-15
|
||||
|
||||
# Transaction Validation
|
||||
transaction_rules:
|
||||
requires_defender_approval: true
|
||||
requires_captain_approval: true # Team Captain must approve team transactions
|
||||
requires_evidence: true # Link to completed work required
|
||||
max_transaction_age_days: 7 # Must be processed within week
|
||||
retroactive_limit_days: 30 # Can't claim SC for work >30 days old
|
||||
|
||||
# Democratic Oversight
|
||||
approval_process:
|
||||
small_transactions_threshold: 100 # SC amounts ≤ 100 need team approval
|
||||
large_transactions_threshold: 200 # SC amounts > 200 need community review
|
||||
founding_work_review_days: 7 # Community review period for founding audit
|
||||
binding_vote_threshold: 3 # Major issues need General Forum vote
|
||||
lazy_consensus_hours: 48 # 48h for community feedback
|
||||
|
||||
# License Integration (Progressive Transparency)
|
||||
license_requirements:
|
||||
campaign_license:
|
||||
can_view_ledger: true
|
||||
can_propose_transactions: false
|
||||
can_approve_transactions: false
|
||||
watch_license:
|
||||
can_view_ledger: true
|
||||
can_propose_transactions: false
|
||||
can_approve_transactions: false
|
||||
work_license:
|
||||
can_view_ledger: true
|
||||
can_propose_transactions: true # Can claim SC for completed tasks
|
||||
can_approve_transactions: false
|
||||
organizational_license:
|
||||
can_view_ledger: true
|
||||
can_propose_transactions: true
|
||||
can_approve_transactions: false # No special approval powers
|
||||
|
||||
# Treasury Protection
|
||||
treasury_validation:
|
||||
max_outstanding_ratio: 3.0 # Outstanding SC ≤ 3x treasury EUR
|
||||
min_treasury_reserve: 1000 # Always keep €1000 emergency reserve
|
||||
redemption_window_trigger: 0.8 # Open redemption at 80% treasury capacity
|
||||
fifo_redemption: true # First earned, first redeemed
|
||||
|
||||
# Quality Assurance
|
||||
quality_rules:
|
||||
requires_task_reference: true # Must link to Forgejo issue/task
|
||||
requires_time_estimate: true # Estimate vs actual hours tracking
|
||||
requires_defender_review: true # Code/work quality confirmation
|
||||
allows_partial_payments: true # Can pay in installments for large tasks
|
||||
|
||||
# Anti-Fraud Measures
|
||||
security_rules:
|
||||
max_daily_sc_per_person: 500 # Prevents gaming
|
||||
requires_captain_countersign: true # Team Captain must approve team payments
|
||||
founder_cap_total: 10000 # Max retroactive SC for any founder
|
||||
community_veto_window: 48 # Hours for community to object
|
||||
permanent_public_record: true # All transactions permanent in git history
|
||||
|
||||
# Integration Rules
|
||||
technical_integration:
|
||||
forgejo_issue_required: true # Must reference actual task
|
||||
element_notification: true # Announce all awards in General Forum
|
||||
engelbot_automation: true # Bot handles routine validations
|
||||
open_collective_sync: true # Treasury sync with OC EU
|
||||
|
||||
# Phase-Specific Rules
|
||||
phase_rules:
|
||||
validation_phase:
|
||||
founding_work_audit: true # Special process for entrepreneur work
|
||||
community_vote_required: true # Major SC awards need approval
|
||||
conservative_rates: true # Start with proven rates
|
||||
design_phase:
|
||||
increased_complexity_bonus: true # Higher rates for complex design work
|
||||
science_team_veto: true # Science team can halt inappropriate awards
|
||||
cross_team_collaboration_bonus: true
|
||||
production_phase:
|
||||
crunch_time_multiplier: 1.2 # 20% bonus during intensive development
|
||||
quality_gates: true # Extra validation for production code
|
||||
user_feedback_integration: true # Bonus for incorporating
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Currency Ledger Report Generator
|
||||
Creates summary reports for community transparency
|
||||
"""
|
||||
|
||||
import csv
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
import os
|
||||
from collections import defaultdict
|
||||
|
||||
def generate_sc_summary():
|
||||
"""Generate SC summary report"""
|
||||
print("📊 SMARTUP CREDITS SUMMARY REPORT")
|
||||
print("=" * 40)
|
||||
|
||||
ledger_file = os.path.join(os.path.dirname(__file__), '..', 'ledger', 'smartup-credits', 'transactions.csv')
|
||||
|
||||
try:
|
||||
with open(ledger_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
|
||||
user_balances = defaultdict(int)
|
||||
total_minted = 0
|
||||
total_redeemed = 0
|
||||
transaction_count = 0
|
||||
|
||||
for row in reader:
|
||||
if row['type'] == 'SC':
|
||||
amount = int(row['amount'])
|
||||
user_balances[row['to']] += amount
|
||||
total_minted += amount
|
||||
transaction_count += 1
|
||||
elif row['type'] == 'REDEEM':
|
||||
amount = int(row['amount'])
|
||||
user_balances[row['from']] -= amount
|
||||
total_redeemed += amount
|
||||
transaction_count += 1
|
||||
|
||||
print(f"Total SC Minted: {total_minted}")
|
||||
print(f"Total SC Redeemed: {total_redeemed}")
|
||||
print(f"SC Outstanding: {total_minted - total_redeemed}")
|
||||
print(f"Total Transactions: {transaction_count}")
|
||||
print(f"Active Contributors: {len([u for u in user_balances if user_balances[u] > 0])}")
|
||||
|
||||
# Top contributors
|
||||
if user_balances:
|
||||
print("\n🏆 TOP SC EARNERS:")
|
||||
sorted_users = sorted(user_balances.items(), key=lambda x: x[1], reverse=True)
|
||||
for i, (user, balance) in enumerate(sorted_users[:5]):
|
||||
if balance > 0:
|
||||
print(f"{i+1}. {user}: {balance} SC")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ No SC transactions found")
|
||||
except Exception as e:
|
||||
print(f"❌ Error generating SC report: {e}")
|
||||
|
||||
def generate_treasury_report():
|
||||
"""Generate treasury health report"""
|
||||
print("\n💰 TREASURY HEALTH REPORT")
|
||||
print("=" * 30)
|
||||
|
||||
treasury_file = os.path.join(os.path.dirname(__file__), '..', 'ledger', 'treasury', 'balance.csv')
|
||||
|
||||
try:
|
||||
with open(treasury_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
|
||||
if rows:
|
||||
latest = rows[-1]
|
||||
eur_balance = float(latest['total_eur_balance'])
|
||||
sc_outstanding = int(latest['sc_outstanding'])
|
||||
sc_liability = float(latest['sc_liability_eur'])
|
||||
|
||||
print(f"💶 EUR Treasury: €{eur_balance:,.2f}")
|
||||
print(f"🪙 SC Outstanding: {sc_outstanding:,} SC")
|
||||
print(f"💸 SC Liability: €{sc_liability:,.2f}")
|
||||
|
||||
if eur_balance > 0:
|
||||
coverage_ratio = eur_balance / sc_liability if sc_liability > 0 else float('inf')
|
||||
print(f"📊 Coverage Ratio: {coverage_ratio:.2f}x")
|
||||
|
||||
if coverage_ratio >= 1.0:
|
||||
print("✅ Full SC redemption possible")
|
||||
else:
|
||||
redemption_capacity = int(eur_balance)
|
||||
print(f"⚠️ Partial redemption: €{redemption_capacity:,} available")
|
||||
|
||||
# 3x rule check
|
||||
max_allowed_sc = eur_balance * 3
|
||||
print(f"🛡️ 3x Rule: {sc_outstanding:,} / {max_allowed_sc:,.0f} SC allowed")
|
||||
|
||||
if sc_outstanding <= max_allowed_sc:
|
||||
print("✅ 3x Rule: COMPLIANT")
|
||||
else:
|
||||
print("🚨 3x Rule: VIOLATION")
|
||||
|
||||
else:
|
||||
print("⚠️ Treasury initialized but no balance records")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ No treasury records found")
|
||||
except Exception as e:
|
||||
print(f"❌ Error generating treasury report: {e}")
|
||||
|
||||
def main():
|
||||
"""Generate all reports"""
|
||||
print("📈 SMARTUP ZERO FINANCIAL REPORTS")
|
||||
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("=" * 50)
|
||||
|
||||
generate_sc_summary()
|
||||
generate_treasury_report()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("💡 All ledger data is public and auditable in git history")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Initialize the founding entrepreneur in Book of Owners
|
||||
This creates the first owner record for Robbert
|
||||
"""
|
||||
|
||||
import csv
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
def initialize_founder():
|
||||
"""Add the founding entrepreneur to Book of Owners"""
|
||||
|
||||
base_path = os.path.join(os.path.dirname(__file__), '..')
|
||||
timestamp = datetime.now().isoformat() + 'Z'
|
||||
|
||||
# Create identity mapping (PRIVATE - not committed)
|
||||
identity_file = os.path.join(base_path, 'ownership', 'identity-mapping.csv')
|
||||
with open(identity_file, 'w', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['owner_id', 'real_name', 'email', 'display_name', 'privacy_level', 'created_date'])
|
||||
writer.writerow(['001', 'Robbert Schep', 'robbert@timeline0.org', 'robbert_founder', 'alias_only', timestamp])
|
||||
|
||||
# Create license transaction record
|
||||
transactions_file = os.path.join(base_path, 'ownership', 'license-transactions.csv')
|
||||
with open(transactions_file, 'w', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['timestamp', 'owner_id', 'transaction_type', 'from_license', 'to_license', 'payment_eur', 'upgrade_reason', 'reference', 'approver'])
|
||||
writer.writerow([timestamp, '001', 'FOUNDER', '', 'work', 0, 'founding_entrepreneur', 'system-init', 'smartup-zero-launch'])
|
||||
|
||||
# Update Book of Owners (PUBLIC)
|
||||
book_file = os.path.join(base_path, 'ownership', 'book-of-owners.csv')
|
||||
with open(book_file, 'w', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['owner_id', 'display_name', 'license_type', 'license_date', 'current_sk', 'voting_weight', 'status', 'last_updated'])
|
||||
writer.writerow(['001', 'robbert_founder', 'work', timestamp, '0', '1.00', 'active', timestamp])
|
||||
|
||||
print("✅ Founding entrepreneur initialized in Book of Owners")
|
||||
print("📝 Identity mapping created (private file)")
|
||||
print("📊 Ready for founding work audit process")
|
||||
|
||||
if __name__ == "__main__":
|
||||
initialize_founder()
|
|
@ -0,0 +1,203 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Book of Owners Management Script
|
||||
Handles license purchases, upgrades, and validation
|
||||
"""
|
||||
|
||||
import csv
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
import os
|
||||
import uuid
|
||||
|
||||
class OwnershipManager:
|
||||
def __init__(self):
|
||||
self.base_path = os.path.join(os.path.dirname(__file__), '..')
|
||||
self.load_policies()
|
||||
|
||||
def load_policies(self):
|
||||
"""Load license policies"""
|
||||
with open(os.path.join(self.base_path, 'policies', 'license-policies.yml'), 'r') as f:
|
||||
self.policies = yaml.safe_load(f)
|
||||
|
||||
def generate_owner_id(self):
|
||||
"""Generate unique owner ID"""
|
||||
# Simple 3-digit incremental ID (could be more sophisticated)
|
||||
book_file = os.path.join(self.base_path, 'ownership', 'book-of-owners.csv')
|
||||
try:
|
||||
with open(book_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
existing_ids = [int(row['owner_id']) for row in reader if row['owner_id'].isdigit()]
|
||||
next_id = max(existing_ids) + 1 if existing_ids else 1
|
||||
return f"{next_id:03d}"
|
||||
except (FileNotFoundError, ValueError):
|
||||
return "001"
|
||||
|
||||
def add_new_owner(self, real_name, email, display_name, license_type, payment_eur=0, privacy_level="alias_only"):
|
||||
"""Add new owner to the system"""
|
||||
owner_id = self.generate_owner_id()
|
||||
timestamp = datetime.now().isoformat() + 'Z'
|
||||
|
||||
# Add to identity mapping (private)
|
||||
identity_file = os.path.join(self.base_path, 'ownership', 'identity-mapping.csv')
|
||||
with open(identity_file, 'a', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow([owner_id, real_name, email, display_name, privacy_level, timestamp])
|
||||
|
||||
# Add to license transactions
|
||||
self.record_license_transaction(owner_id, "PURCHASE", "", license_type, payment_eur, "initial_purchase")
|
||||
|
||||
# Update book of owners
|
||||
self.update_book_of_owners(owner_id, display_name, license_type)
|
||||
|
||||
print(f"✅ New owner added: {display_name} ({owner_id}) - {license_type} license")
|
||||
return owner_id
|
||||
|
||||
def record_license_transaction(self, owner_id, transaction_type, from_license, to_license, payment_eur, reason, reference="manual", approver="system"):
|
||||
"""Record license change in transaction log"""
|
||||
timestamp = datetime.now().isoformat() + 'Z'
|
||||
|
||||
transactions_file = os.path.join(self.base_path, 'ownership', 'license-transactions.csv')
|
||||
with open(transactions_file, 'a', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow([timestamp, owner_id, transaction_type, from_license, to_license, payment_eur, reason, reference, approver])
|
||||
|
||||
def update_book_of_owners(self, owner_id, display_name, license_type, current_sk=0, status="active"):
|
||||
"""Update current book of owners record"""
|
||||
timestamp = datetime.now().isoformat() + 'Z'
|
||||
voting_weight = min(1.5, 1 + (current_sk / 1000))
|
||||
|
||||
book_file = os.path.join(self.base_path, 'ownership', 'book-of-owners.csv')
|
||||
|
||||
# Read existing records
|
||||
records = []
|
||||
try:
|
||||
with open(book_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
records = [row for row in reader if row['owner_id'] != owner_id]
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Add/update record
|
||||
records.append({
|
||||
'owner_id': owner_id,
|
||||
'display_name': display_name,
|
||||
'license_type': license_type,
|
||||
'license_date': timestamp,
|
||||
'current_sk': current_sk,
|
||||
'voting_weight': f"{voting_weight:.2f}",
|
||||
'status': status,
|
||||
'last_updated': timestamp
|
||||
})
|
||||
|
||||
# Write back
|
||||
with open(book_file, 'w', newline='') as f:
|
||||
fieldnames = ['owner_id', 'display_name', 'license_type', 'license_date', 'current_sk', 'voting_weight', 'status', 'last_updated']
|
||||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
writer.writerows(records)
|
||||
|
||||
def check_sk_upgrades(self):
|
||||
"""Check for automatic SK-based license upgrades"""
|
||||
print("🔍 Checking for automatic license upgrades...")
|
||||
|
||||
# Load current SK balances from karma ledger
|
||||
sk_balances = self.get_current_sk_balances()
|
||||
|
||||
# Check each owner for upgrade eligibility
|
||||
book_file = os.path.join(self.base_path, 'ownership', 'book-of-owners.csv')
|
||||
with open(book_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for owner in reader:
|
||||
owner_id = owner['owner_id']
|
||||
current_license = owner['license_type']
|
||||
display_name = owner['display_name']
|
||||
current_sk = sk_balances.get(display_name, 0)
|
||||
|
||||
# Check campaign -> watch upgrade
|
||||
if current_license == 'campaign' and current_sk >= 100:
|
||||
print(f"🎉 Auto-upgrade: {display_name} (Campaign → Watch) - {current_sk} SK earned!")
|
||||
self.record_license_transaction(owner_id, "UPGRADE_SK", "campaign", "watch", 0, "sk_threshold_100")
|
||||
self.update_book_of_owners(owner_id, display_name, "watch", current_sk)
|
||||
|
||||
def get_current_sk_balances(self):
|
||||
"""Get current SK balances from karma ledger"""
|
||||
balances = {}
|
||||
karma_file = os.path.join(self.base_path, 'ledger', 'social-karma', 'transactions.csv')
|
||||
|
||||
try:
|
||||
with open(karma_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
if row['type'] == 'KARMA':
|
||||
balances[row['to']] = balances.get(row['to'], 0) + int(row['amount'])
|
||||
elif row['type'] == 'DECAY':
|
||||
balances[row['from']] = balances.get(row['from'], 0) - int(row['amount'])
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
return balances
|
||||
|
||||
def validate_currency_award(self, username, currency_type, amount):
|
||||
"""Validate if user can receive SC or SK award"""
|
||||
# Get user's license type
|
||||
book_file = os.path.join(self.base_path, 'ownership', 'book-of-owners.csv')
|
||||
|
||||
try:
|
||||
with open(book_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for owner in reader:
|
||||
if owner['display_name'] == username:
|
||||
license_type = owner['license_type']
|
||||
|
||||
# Check SC eligibility
|
||||
if currency_type == 'SC':
|
||||
can_earn = self.policies['license_capabilities'][license_type]['can_earn_sc']
|
||||
if not can_earn:
|
||||
return False, f"{username} has {license_type} license - cannot earn SC (needs work/organizational)"
|
||||
|
||||
# Check SK eligibility
|
||||
elif currency_type == 'SK':
|
||||
can_earn = self.policies['license_capabilities'][license_type]['can_earn_sk']
|
||||
if not can_earn:
|
||||
return False, f"{username} has {license_type} license - cannot earn SK"
|
||||
|
||||
return True, f"✅ {username} eligible for {amount} {currency_type}"
|
||||
|
||||
return False, f"❌ {username} not found in Book of Owners"
|
||||
|
||||
except FileNotFoundError:
|
||||
return False, "❌ Book of Owners not found"
|
||||
|
||||
def main():
|
||||
"""Main owner management interface"""
|
||||
manager = OwnershipManager()
|
||||
|
||||
print("📖 BOOK OF OWNERS MANAGEMENT")
|
||||
print("=" * 40)
|
||||
print("1. Add new owner")
|
||||
print("2. Check SK upgrade eligibility")
|
||||
print("3. Validate currency award")
|
||||
print("4. Show current owners")
|
||||
|
||||
# For now, just show current owners
|
||||
book_file = os.path.join(manager.base_path, 'ownership', 'book-of-owners.csv')
|
||||
try:
|
||||
with open(book_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
owners = list(reader)
|
||||
|
||||
if owners:
|
||||
print(f"\n📊 Current Owners: {len([o for o in owners if o['owner_id']])}")
|
||||
print("-" * 60)
|
||||
for owner in owners:
|
||||
if owner['owner_id']: # Skip header comments
|
||||
print(f"{owner['owner_id']}: {owner['display_name']} ({owner['license_type']}) - {owner['current_sk']} SK")
|
||||
else:
|
||||
print("📝 No owners registered yet")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("📝 Book of Owners not created yet")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Currency Ledger Validation Script
|
||||
Ensures ledger integrity and policy compliance
|
||||
"""
|
||||
|
||||
import csv
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
def load_policies():
|
||||
"""Load policy configuration files"""
|
||||
policies = {}
|
||||
policy_dir = os.path.join(os.path.dirname(__file__), '..', 'policies')
|
||||
|
||||
with open(os.path.join(policy_dir, 'credit-rates.yml'), 'r') as f:
|
||||
policies['credit_rates'] = yaml.safe_load(f)
|
||||
|
||||
with open(os.path.join(policy_dir, 'karma-rules.yml'), 'r') as f:
|
||||
policies['karma_rules'] = yaml.safe_load(f)
|
||||
|
||||
with open(os.path.join(policy_dir, 'validation-rules.yml'), 'r') as f:
|
||||
policies['validation'] = yaml.safe_load(f)
|
||||
|
||||
return policies
|
||||
|
||||
def validate_sc_ledger():
|
||||
"""Validate Smartup Credits ledger"""
|
||||
print("🔍 Validating Smartup Credits ledger...")
|
||||
|
||||
total_sc = 0
|
||||
ledger_file = os.path.join(os.path.dirname(__file__), '..', 'ledger', 'smartup-credits', 'transactions.csv')
|
||||
|
||||
try:
|
||||
with open(ledger_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
transaction_count = 0
|
||||
|
||||
for row in reader:
|
||||
if row['type'] == 'SC':
|
||||
total_sc += int(row['amount'])
|
||||
transaction_count += 1
|
||||
elif row['type'] == 'REDEEM':
|
||||
total_sc -= int(row['amount'])
|
||||
transaction_count += 1
|
||||
|
||||
print(f"✅ Total SC Outstanding: {total_sc}")
|
||||
print(f"✅ Transactions Processed: {transaction_count}")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ SC ledger file not found")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error validating SC ledger: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def validate_treasury():
|
||||
"""Validate treasury balance and 3x rule"""
|
||||
print("🔍 Validating treasury balance...")
|
||||
|
||||
treasury_file = os.path.join(os.path.dirname(__file__), '..', 'ledger', 'treasury', 'balance.csv')
|
||||
|
||||
try:
|
||||
with open(treasury_file, 'r') as f:
|
||||
reader = csv.DictReader(f)
|
||||
rows = list(reader)
|
||||
|
||||
if rows:
|
||||
latest = rows[-1]
|
||||
eur_balance = float(latest['total_eur_balance'])
|
||||
sc_outstanding = int(latest['sc_outstanding'])
|
||||
ratio = float(latest['ratio'])
|
||||
|
||||
print(f"✅ EUR Treasury: €{eur_balance}")
|
||||
print(f"✅ SC Outstanding: {sc_outstanding} SC")
|
||||
print(f"✅ Current Ratio: {ratio:.2f}")
|
||||
|
||||
# Check 3x rule
|
||||
if sc_outstanding <= (eur_balance * 3):
|
||||
print("✅ 3x Rule: COMPLIANT")
|
||||
else:
|
||||
print("❌ 3x Rule: VIOLATION - Too much SC outstanding!")
|
||||
return False
|
||||
else:
|
||||
print("⚠️ Treasury initialized but no transactions yet")
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ Treasury file not found")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Error validating treasury: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""Main validation function"""
|
||||
print("🚀 Smartup Zero Currency Ledger Validation")
|
||||
print("=" * 50)
|
||||
|
||||
# Load policies
|
||||
try:
|
||||
policies = load_policies()
|
||||
print("✅ Policy files loaded successfully")
|
||||
except Exception as e:
|
||||
print(f"❌ Error loading policies: {e}")
|
||||
return
|
||||
|
||||
# Validate ledgers
|
||||
sc_valid = validate_sc_ledger()
|
||||
treasury_valid = validate_treasury()
|
||||
|
||||
print("=" * 50)
|
||||
if sc_valid and treasury_valid:
|
||||
print("🎉 All validations PASSED - Ledger is healthy!")
|
||||
else:
|
||||
print("🚨 Validation FAILED - Check errors above")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue