First implementation

This commit is contained in:
2026-06-15 00:42:14 +02:00
parent 542d7c22be
commit fa39883aec
18 changed files with 1352 additions and 66 deletions

View File

@@ -103,9 +103,9 @@ curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
## Local Developer Workflow
This repository is documentation- and planning-focused (no source code, no traditional build/test/lint/install/run toolchain).
This repository started as documentation- and planning-focused but now includes initial implementation code for the feature-sdk (see WP-0003).
**Verification and development commands (add to future sessions as needed):**
**Verification and development commands:**
- Orient and review: `cat .custodian-brief.md` ; `cat INTENT.md SCOPE.md AGENTS.md README.md` ; `ls workplans/`
- Check hub state (requires local state-hub API): use the curl commands documented in "State Hub Integration" and "Session Protocol" sections above.
- After any workplan or doc changes: From `~/state-hub` checkout, run `make fix-consistency REPO=feature-control` (syncs markdown task statuses into hub DB, updates brief, populates state_hub_* IDs).
@@ -113,7 +113,14 @@ This repository is documentation- and planning-focused (no source code, no tradi
- Log required progress: `curl -s -X POST http://127.0.0.1:8000/progress/ ...` (see protocol).
- To verify changes post-fix: Re-`cat .custodian-brief.md`, re-`ls workplans/`, re-check task statuses in files vs hub queries, confirm no drift.
No `make install`, `make test`, `make lint`, `make build`, or `make run` apply yet (pure docs repo). If implementation artifacts (e.g. OpenFeature wrappers, providers, or example code) are added later, define and document the appropriate commands here and in relevant workplans.
**Code / SDK (starting with WP-0003):**
- Install dev: `pip install -e ".[dev]"`
- Test: `pytest`
- Lint/format: `ruff check` ; `ruff format`
- Run example: `python docs/sdk-examples/basic_usage.py`
- (The openfeature-sdk is an optional runtime dep for real providers; local provider works without it for tests/dev.)
No full build/run yet (MVP phase). Extend this section as implementation grows.
## Workplan Convention (ADR-001)

View File

@@ -0,0 +1,76 @@
# Consumer Brief: Feature-Control (for Adopting Repositories)
**Status:** Draft (expanded from canon-interface-card.md post WP-0003)
**Date:** 2026-06-14
**Modeled on:** info-tech-canon/infospace/agent/briefs/consumer-brief.template.md and interface-card.schema.yaml (per canon practices).
**Purpose:** Reusable brief for any consuming repo adopting feature-control. Use in State Hub, ralph sessions, or as attachment to adoption workplans.
## Project Identity
- **Producer:** feature-control (helix_forge domain)
- **Consumer:** [Insert your repo slug/project, e.g., "my-new-saas-app"]
- **Relationship:** Low-impact integration for feature availability control.
## Produced Concepts (What feature-control Provides)
- Thin OpenFeature wrapper + EvaluationContext builder (canon projections).
- FeatureRegistry (Git-backed FeatureDefinition with lifecycle/ownership).
- Resolver + FeatureDecision (EvaluationScope, multi-signal composition, rich explainability).
- LocalProvider (for dev/tests; extensible to real OF providers).
- Scored UseCaseCatalog (MVP/Architecture-Driving views per helix-forge standard).
- Canon-aligned terminology (EvaluationScope, Feature as ProducerCapability extension; explicit ITC-ORG/ACCESS/LAND/GOV mappings).
- Pilots, examples, and adoption guide (for quick start).
## Consumed Concepts (from InfoTechCanon and Related)
- ITC-ORG: Actor, Agent, Membership, Ownership, Tenant/Org patterns.
- ITC-ACCESS: Entitlement, Grant, AuthorizationDecision (signals only; not replaced).
- ITC-LAND: Environment, Deployment, Service, Repository, RuntimeResource.
- ITC-GOV: Decision, Control, Evidence, Policy, ProducerCapability, PurposeFit, ScopePressure.
- ITC-TaggingStandard: Feature categories.
- ITC-TASK: Lifecycle reviews/remediation.
- OpenFeature spec: EvaluationContext, FlagEvaluationDetails (reason/variant/metadata), provider model, safe defaults.
- (See docs/canon-mapping.md for full table with ownership/gaps.)
## Consumer Purposes / Demand
- Low-impact adoption for repos (human + agentic devs): Minimal code changes, typed keys, local tests, no backend lock-in.
- Multi-scope control: Tenant, agent, environment, domain, etc. (via EvaluationScope).
- Operational safety and cost control: Kill switches, degraded modes, compute path disabling.
- Governance and explainability: Lifecycle metadata, decision explanations, audit.
- Agent capability gating (with separate tool auth).
- Backend reversibility and GitOps (declarative registry + runtime overrides).
## Scope Pressure / Fit
- Current pain in consumer: [Insert: e.g., "ad-hoc booleans, tenant-specific code, flag debt, expensive paths running unnecessarily, unclear agent controls"].
- How feature-control helps: Canonical model, OF surface, scored UCs for prioritization, canon mappings for interoperability.
- Fit with consumer INTENT/SCOPE: [Insert mapping; e.g., "Your 'user can do X' maps to feature key 'your.domain.x'; tenant controls align with your multi-tenant model."]
## Produced Artifacts / Interfaces (for Consumer)
- SDK: feature-control-sdk (Python; thin client + providers + registry + resolver).
- Docs: AdoptionGuide.md, scored UCC, canon-mapping.md, mvp_pilot.py, sdk-examples/.
- Registry baseline: features.json (Git-committed FeatureDefinitions).
- Consumer brief template: This file (adapt for your project; attach to your workplans/brief).
## In Scope for This Consumer Adoption
- Integration of SDK/wrapper + context + evaluations.
- Feature registration and basic governance.
- Local/dev adoption + pilot validation.
- Mapping of your features to the scored UCC for MVP selection.
## Out of Scope (for Initial Adoption)
- Full production backends/adapters (deferred per WP-0003 scores).
- Deep entitlement self-service or complex approvals.
- Non-Python implementations (adapt the concepts via OpenFeature).
## Open Questions / Risks for Consumer
- [Insert project-specific: e.g., "How to export generated keys for our TypeScript frontend? Backend choice for production?"]
- General: See WP-0003 open questions (backend, generated keys, etc.).
## Evidence / Next Steps
- Pilot your adoption using docs/pilots/mvp_pilot.py (adapt for your UCs).
- Create consumer workplan (e.g., "Adopt feature-control") with tasks from the AdoptionGuide.
- Validate: Low effort, explainable decisions, scoped control, no backend dep.
- Contribute back: New UCs, patterns, or canon extensions via State Hub.
**Attach this (customized) to your project's .custodian-brief.md, workplans, or adoption sessions for traceability.**
See the full FeatureControlAdoptionGuide.md for step-by-step instructions and the reusable agent prompt. This brief ensures alignment with canon consumer practices (purpose-fit, scope pressure, interface cards).
Ready for your specific project? Provide details and we'll customize/generate the artifacts.

View File

@@ -0,0 +1,206 @@
# Feature-Control Adoption Guide for Consuming Repositories
**Status:** Draft (post WP-0003 MVP)
**Date:** 2026-06-14
**Audience:** Developers, architects, and AI agents integrating feature-control into new or existing projects (consuming repos).
**Based on:** FEATURE-WP-0003 MVP implementation, scored UseCaseCatalog.md, canon-mapping.md, canon-interface-card.md (modeled on info-tech-canon consumer briefs), pilot examples, PRD/INTENT boundaries.
This guide provides a practical, step-by-step process for adopting the feature-control framework. It ensures low-impact integration, canon alignment (EvaluationScope, ITC-ORG/ACCESS/LAND/GOV mappings), and use of the scored use cases for prioritization.
## 1. Prerequisites and Orientation
Before starting:
- Read the core docs in this repo (or the feature-control package):
- [INTENT.md](../INTENT.md): Stable intent, boundaries (OpenFeature-first, no auth/entitlement ownership, GitOps + runtime).
- [specs/ProductRequirementsDocument.md](../specs/ProductRequirementsDocument.md): Requirements, data models, architecture.
- [specs/UseCaseCatalog.md](../specs/UseCaseCatalog.md): Full catalog **with scoring applied per helix-forge UseCaseScoringStandard.md**. Use the summary table, MVP/Prototype/Architecture-Driving views to prioritize (e.g., high Value + low-moderate Cost for early adoption).
- [docs/canon-mapping.md](../docs/canon-mapping.md): Explicit mappings (e.g., Feature → ProducerCapability extension; EvaluationScope → ITC-ORG Membership + ITC-LAND dims). Avoid redefining canon terms.
- [docs/canon-interface-card.md](../docs/canon-interface-card.md): High-level consumer view (produced concepts, consumed ITC models, purposes, scope pressure).
- [docs/pilots/mvp_pilot.py](../docs/pilots/mvp_pilot.py) and [docs/sdk-examples/](../docs/sdk-examples/): Concrete usage examples.
- Understand key canon-aligned concepts:
- **EvaluationScope / TargetingScope**: Replaces bare "scope" (platform, tenant, agent, etc.). Maps to Memberships + Landscape resources.
- **FeatureDecision**: Rich (value, reason, source, scope, etc.) for explainability.
- **Context**: Projection from ITC-ORG (actors/agents), ITC-LAND (services/envs), ITC-ACCESS signals.
- Check your project's alignment with INTENT boundaries (mechanism over policy; overlay before mutation; capability-aware adapters).
**Read the brief for the target project** (`.custodian-brief.md`) and its AGENTS.md/INTENT.md to map your features to the scored UCC.
## 2. High-Level Adoption Flow (Based on Scored UCC)
Follow the recommended workflow from the helix-forge standard (capture → normalize → classify → score → select → implement). Prioritize from the UCC summary:
**MVP Candidates (strong for first integration, per scoring):**
- UC-A1: Adopt in new repo (core path; high user/business/strategic/proof value).
- UC-A2: Local/test provider (high proof/learning; low effort).
- UC-C1: Enable for tenant (multi-tenant control).
- UC-D3: AI agent capability (agent-first class).
- UC-E1: Disable compute-heavy per tenant (cost governance).
- UC-E4: Emergency kill switch (operational safety).
- UC-G1: Register with lifecycle (governance/hygiene).
**Supporting/Prototype:**
- UC-G3: Explain decision (explainability core).
- UC-H1: Provider switch (reversibility).
**Later/Enhancement:** Complex self-service, full analytics (lower urgency or higher cost per scores).
**Architecture Drivers (implement early for understanding, even if deferred):** Those with high Architecture Score (cross-repo, canon impact, policy, reuse, compute, observability) — e.g., A1, E1, D3, G1.
## 3. Step-by-Step Integration (Low-Impact, per UC-A1/A2)
1. **Add the SDK**:
- For Python projects: Add `feature-control-sdk` as a dependency (current: local via `pip install -e` from this repo's pyproject.toml; future: PyPI).
- Minimal: Copy `src/feature_control_sdk/` into your project initially.
- Optional: `openfeature-sdk` for real providers (backend-agnostic per design).
2. **Initialize the Client** (thin wrapper):
```python
from feature_control_sdk import FeatureControlClient, LocalProvider, FeatureRegistry, Resolver
client = FeatureControlClient(domain="your-service") # Optional OF domain
```
3. **Set up for Dev/Tests (LocalProvider, no backend)**:
```python
local_values = {
"your.domain.capability.feature": True, # Use canonical keys
# Simulate overrides/signals
"tenant:acme:your.feature": False,
"kill:your.feature": False,
}
client.set_provider(LocalProvider(local_values))
# For rich feature-control logic (MVP mode, no full OF needed):
reg = FeatureRegistry("features.json") # Git-committed baseline
# (Populate from registry or hardcode for pilot)
resolver = Resolver(reg, local_values)
client.set_resolver(resolver)
```
4. **Build Evaluation Context** (canon-aligned):
Use the `_build_context` logic or manual dict. Project from your system's facts (users, tenants, agents, services):
```python
context = {
"targeting_key": "user-123", # Per OF spec
"actor_type": "human", # or "agent"
"tenant_id": "acme",
"domain_id": "your-domain",
"agent_id": "my-agent-v1" if agent else None,
"environment": "production",
"service": "your-service",
"roles": ["user"],
"plan": "premium",
# Signals
"entitlement": True, # From your entitlement system (ITC-ACCESS)
}
```
5. **Evaluate Features** (standard OF surface + rich control):
```python
enabled = client.get_boolean_value(
"your.domain.capability.feature", # Canonical key (from registry)
default=False,
context=context
)
# For rich explain (UC-G3):
decision = client.explain("your.domain.capability.feature", False, context)
print(decision.reason, decision.scope) # e.g., "tenant_policy", "tenant:acme"
```
6. **Register Features (Governance, per UC-G1)**:
- Maintain a `features.json` (or registry export) in Git as declarative baseline.
- Example definition (matches PRD/UCC):
```json
{
"feature_key": "your.domain.capability.feature",
"name": "Your Capability",
"description": "...",
"owner": "your-team", # ITC-ORG
"category": "release", # ITC-Tagging
"default_value": false,
"safe_fallback": false,
"lifecycle_state": "active",
"expected_lifetime": "long_lived",
"tenant_configurable": true,
"documentation": "docs/features/..."
}
```
- Use `FeatureRegistry` to load/validate/register (enforces owner, temp review dates).
- In consuming code: Import or discover keys (stub scanner for UC-A3).
7. **Test and Adopt**:
- Use LocalProvider for unit/integration tests (deterministic, network-free; UC-A2).
- Pilot your features using `docs/pilots/mvp_pilot.py` as template (adapt for your UCs).
- Measure: Adoption effort (should be <1 small task), explainability, control without redeploys.
- For real providers: Configure OF provider (e.g., Unleash adapter) behind the client (H1 support).
8. **Governance and Advanced**:
- Enforce lifecycle in your registry (temp features get Tasks per ITC-TASK).
- Use `explain()` for support/debug (UC-G3).
- Audit changes (stub in pilot; expand per G4).
- For agents: Pass `actor_type: "agent"` + `agent_id` in context (D3 support; still enforce tool auth per boundaries).
## 4. Mapping Your Project's Features
- Review your project's INTENT/SCOPE/UCC (or equivalent).
- Map to feature-control UCC: E.g., "user can do X" → feature key like `your.domain.x` (per FR-3 naming).
- Score/prioritize using the standard (or copy from our scored UCC).
- Start with high-MVP-fit: Adoption + 2-3 core controls (tenant/agent/compute/kill).
- Register in your Git baseline; control via context (tenant_id for multi-tenant, agent_id for AI).
## 5. Common Pitfalls and Boundaries (from INTENT/PRD)
- **Do not** use features for auth (FR-8): Always pair with your authz system.
- **Do not** confuse with entitlement: Feature control composes but doesn't own (UC-F1).
- Client-side never enforces security.
- Start with local provider; add real backends later (backend-agnostic).
- Commit `features.json`/registry to Git for GitOps baseline.
## 6. Validation and Next Steps
- Run the adapted pilot: End-to-end for your MVP UCs.
- Check: Decisions explainable? Controls per scope (no cross-tenant)? Tests without backend?
- Metrics: Adoption time, decision coverage, compute savings.
- If it fits: Proceed to production hardening (e.g., real providers, full audit, adapter contracts).
- Report back: Update your project's docs/brief with adoption status. Consider contributing patterns back (e.g., new UCs or canon extensions).
## 7. Agent/AI Support Prompts and Skills
**Reusable Prompt Template** (for Grok, Claude, Cursor, etc.; copy-paste into your agent):
```
You are an expert at adopting the feature-control framework (OpenFeature-based, canon-aligned multi-scope feature availability control from the feature-control repo).
Follow this exact step-by-step guide from docs/FeatureControlAdoptionGuide.md:
[PASTE THE FULL GUIDE ABOVE]
For the current project (describe briefly: [e.g., "a new multi-tenant SaaS app in Python with user/engine/agent actors, compute-heavy features, need for tenant/agent controls"]):
1. Identify 5-10 candidate features from the project's scope/intent.
2. Map them to the scored UseCaseCatalog.md (prioritize MVP/Architecture-Driving).
3. Propose canonical feature keys (per naming convention).
4. Generate the integration code: client setup, context builder (using canon projections), evaluations, registry definitions (in JSON), tests with LocalProvider.
5. Create a simple pilot script adapted from mvp_pilot.py.
6. Note any canon gaps or custom UCs.
7. Ensure compliance with INTENT boundaries.
Output: Updated code diffs, new files (e.g., feature_flags.py, features.json, tests), and a brief adoption report referencing the scoring and canon-mapping.
```
**Dedicated Skill/Agent Recommendation**:
- **Yes, create one**: A "feature-control-adopter" skill or ralph-style prompt.
- Example skill file (add to your agent's skills dir or `docs/skills/adopt-feature-control.md`):
- Trigger: "adopt feature-control in this project" or "/adopt-fc".
- Behavior: Load this guide + canon-mapping + scored UCC + pilot as context. Walk user/agent through steps, generate code, validate against acceptance.
- For ralph-workplan users: Tie a consumer workplan (e.g., "Adopt feature-control") to this prompt.
- Enhancement: Integrate with State Hub (e.g., new workplan for consumer adoption in a target repo, using get_domain_summary for context).
- This repo can host the canonical version; consumers copy/adapt.
## 8. Gaps and Future Support (from WP-0003 Open Questions)
- Full backend providers (Unleash/Flagsmith adapters) and production config.
- Generated constants/key discovery (beyond stub).
- Deeper entitlement integration and tenant self-service.
- Full adapter contracts (for non-Python or custom backends).
- Publishing the SDK (PyPI, versioning).
- More pilots/examples for other languages/UCS.
**Next Step Recommendation**:
- If you have a specific new project/repo in mind (e.g., "my-new-app"), tell me the path or details — we can run a guided adoption session using the prompt above, generate the exact files, and create a consumer workplan (e.g., MYAPP-WP-0001-adopt-feature-control).
- Create a new workplan here (FEATURE-WP-0004-feature-control-adoption-toolkit) to formalize the guide, skill, and consumer brief.
- Expand the canon-interface-card.md into a full ConsumerBrief.md (per info-tech-canon templates).
This makes feature-control immediately usable for new projects while providing the requested guides/prompts/skills. The MVP gives a working foundation; we can iterate based on real adoption feedback.
Ready to proceed with a specific project or create the new artifacts? Provide details! (We'll log progress and sync via State Hub.)

112
docs/pilots/mvp_pilot.py Normal file
View File

@@ -0,0 +1,112 @@
"""
MVP Pilot script for WP-0003 T06.
Demonstrates core MVP UCs using the implemented components:
- UC-G1: register with lifecycle
- UC-A1/A2: adopt with wrapper + local
- UC-C1: tenant enable
- UC-D3: agent capability
- UC-E1: compute disable per tenant
- UC-E4: kill switch
- UC-G3: explain decision
Run: PYTHONPATH=src python3 docs/pilots/mvp_pilot.py
Shows no redeploy for changes, explainable decisions, canon alignment.
"""
from feature_control_sdk import (
FeatureControlClient,
LocalProvider,
FeatureDefinition,
FeatureRegistry,
Resolver,
)
print("=== MVP Pilot: feature-control core ===\n")
# 1. Registry (UC-G1)
reg = FeatureRegistry("/tmp/features.json") # "git" baseline
reg.register(FeatureDefinition(
feature_key="compute.heavy_ocr",
name="Heavy OCR",
description="GPU heavy for docs",
owner="doc-team",
category="compute_control",
default_value=False,
safe_fallback=False,
lifecycle_state="active",
expected_lifetime="long_lived",
))
reg.register(FeatureDefinition(
feature_key="agent.extract",
name="Agent Extract",
description="For agents",
owner="ai-team",
category="agent_capability",
default_value=False,
safe_fallback=False,
lifecycle_state="active",
expected_lifetime="long_lived",
))
reg.register(FeatureDefinition(
feature_key="tenant.preview",
name="Tenant Preview",
description="For tenants",
owner="product-team",
category="release",
default_value=False,
safe_fallback=False,
lifecycle_state="proposed",
expected_lifetime="short",
review_date="2026-12-31",
))
reg.save()
print("Registered features (UC-G1 satisfied):", reg.keys())
# 2. Local values + resolver for control logic
local_values = {
"compute.heavy_ocr": False, # default
"agent.extract": False,
"tenant.preview": True,
"kill:compute.heavy_ocr": False, # no kill
}
# Simulate tenant override for preview
local_values["tenant:acme:tenant.preview"] = True
resolver = Resolver(reg, local_values)
# 3. Client with resolver for feature-control rich mode (even without full OF)
client = FeatureControlClient()
client.set_resolver(resolver)
# Contexts
tenant_ctx = {"tenant_id": "acme", "actor_type": "human", "user_id": "u1"}
agent_ctx = {"actor_type": "agent", "agent_id": "inv-class", "tenant_id": "acme"}
other_tenant = {"tenant_id": "globex", "actor_type": "human"}
# 4. Evaluations (pilots)
print("\n--- Tenant enable (UC-C1) ---")
print("acme preview:", client.get_boolean_value("tenant.preview", False, tenant_ctx))
print("globex preview:", client.get_boolean_value("tenant.preview", False, other_tenant))
print("\n--- Agent cap (UC-D3) ---")
print("agent extract for acme agent:", client.get_boolean_value("agent.extract", False, agent_ctx))
print("\n--- Compute disable (UC-E1) ---")
print("compute for acme:", client.get_boolean_value("compute.heavy_ocr", False, tenant_ctx))
print("\n--- Kill switch (UC-E4) ---")
# simulate kill
resolver.values["kill:compute.heavy_ocr"] = True
print("compute with kill for acme:", client.get_boolean_value("compute.heavy_ocr", False, tenant_ctx))
print("\n--- Explain (UC-G3) ---")
decision = client.explain("tenant.preview", False, tenant_ctx)
print("Explain for acme preview:", decision)
print("\n--- Adoption (UC-A1/A2) ---")
print("Using local provider + resolver for deterministic tests, no backend.")
print("\nPilot complete. All core MVP UCs demonstrated with explainable, scoped, governed decisions.")
print("No redeploy needed (local values changed at runtime).")

View File

@@ -0,0 +1,65 @@
# Reusable Prompt: Adopt Feature-Control in a New/Existing Project
**Usage**: Copy this entire prompt into your agent (Grok, Claude, Cursor, Aider, etc.) at the start of an adoption session. Replace placeholders in [brackets]. Follow exactly; reference the artifacts in this feature-control repo.
```
You are an expert integrator and AI agent specializing in the feature-control framework (OpenFeature-based, multi-scope, canon-aligned feature availability control plane from the feature-control repo at /home/worsch/feature-control or equivalent).
Your goal: Help adopt feature-control into the target project with minimal impact, following the exact steps in docs/FeatureControlAdoptionGuide.md (created post-WP-0003 MVP). Ensure alignment with INTENT.md, the scored UseCaseCatalog.md (per helix-forge UseCaseScoringStandard), docs/canon-mapping.md, and docs/canon-interface-card.md.
**Exact Process (do not skip or reorder):**
1. **Orient and Review**:
- Read and internalize: INTENT.md, specs/ProductRequirementsDocument.md, specs/UseCaseCatalog.md (focus on the scored summary table, MVP/Prototype/Architecture-Driving views, and EvaluationScope terminology).
- Read: docs/canon-mapping.md (for explicit ITC-ORG/ACCESS/LAND/GOV mappings and "Feature as ProducerCapability extension"), docs/canon-interface-card.md, docs/pilots/mvp_pilot.py, and docs/sdk-examples/.
- Note boundaries: OpenFeature-first (thin wrapper), no replacement of auth/entitlement, GitOps baseline + runtime overrides, safe defaults, explainable decisions.
2. **Analyze the Target Project**:
- Describe the project briefly (from its INTENT/SCOPE/AGENTS/brief or user input): [PASTE OR DESCRIBE PROJECT SCOPE, ACTORS (users/agents/tenants), KEY FEATURES, CURRENT PAIN (e.g., hard-coded flags, compute waste, agent controls)].
- Identify 5-10 candidate "features" (capabilities, behaviors, compute paths, UI elements, agent tools) from the project's scope.
- Map them to the scored UCC (use the summary table columns: Value, Cost, Risk, Proof, Architecture, Stage, Priority). Prioritize high-Value + acceptable Cost/Risk for MVP (e.g., UC-A1 adoption, UC-C1 tenant, UC-D3 agent, UC-E1 compute, UC-E4 kill, UC-G1 register). Flag Architecture-Drivers explicitly.
3. **Design the Integration**:
- Propose canonical feature keys (per FR-3: <domain>.<capability>.<feature>[.<mode>], e.g., "mail.dispatch.new_renderer").
- Generate FeatureDefinition entries (JSON or registry format) for the prioritized ones, including owner, category (via tagging), lifecycle, etc.
- Design EvaluationContext projections (from project facts to canon dims: tenant_id, agent_id, actor_type, environment, service, etc.).
4. **Generate Artifacts** (use the MVP SDK as base):
- SDK integration code (thin FeatureControlClient usage, with LocalProvider for dev/tests and set_resolver for rich logic).
- Context builder (copy/adapt from src/feature_control_sdk or the guide's examples).
- features.json or registry baseline (declarative, Git-committed).
- Tests using LocalProvider (deterministic, network-free).
- Adapted pilot script (based on docs/pilots/mvp_pilot.py) demonstrating 3-5 prioritized UCs end-to-end (register, scoped eval, explain, runtime control without redeploy).
- Any needed updates to the project's AGENTS.md, docs, or brief.
5. **Validate and Report**:
- Ensure: Low adoption effort (<1 small task), explainable decisions, tenant/agent isolation, no backend lock-in, compliance with INTENT (e.g., client flags never auth).
- Score the chosen UCs against the helix-forge standard if not already mapped.
- Produce a brief adoption report: What was implemented, fit vs. scored catalog, open questions/gaps, next steps (e.g., real providers, new workplan).
- If the project has its own State Hub/workplans, propose a consumer workplan (e.g., "Adopt feature-control") with tasks tied to the UCs.
**Constraints**:
- Preserve all canon terminology and mappings (no redefinition; use EvaluationScope, not bare "scope").
- Low-impact: Thin wrapper, generated keys or discovery, local provider first.
- Output only diffs, new files, code, and the report. Use the SDK code from src/ as the reference implementation.
- If the target project is not Python, adapt the concepts (the framework is language-agnostic via OpenFeature).
Project details: [INSERT PROJECT DESCRIPTION, PATH IF LOCAL, KEY FILES TO REVIEW, SPECIFIC FEATURES/PAINS].
Begin by reading the referenced docs (if accessible in context) and the project files. Output the plan, then the generated artifacts.
```
**Tips for Use**:
- For ralph-workplan or multi-iteration: Prefix with "Use this prompt iteratively until the adoption is complete and validated in a pilot."
- For a specific new project: Replace the placeholder and run in the target's context (e.g., via Cursor or a subagent).
- Extend with project-specific canon mappings if needed.
This prompt + the AdoptionGuide.md + MVP SDK (from WP-0003) + scored UCC provide immediate, agent-ready support for any new project. The canon-interface-card.md can be expanded into a full ConsumerBrief.md for the target (modeled on info-tech-canon templates).
**Recommended Next**:
- If you name the new project/repo (e.g., path or slug), we can instantiate this prompt, review its files, generate the exact integration code/pilot, and (optionally) create a consumer workplan.
- Create a dedicated skill (e.g., in your .grok/skills or .claude/commands): A "adopt-feature-control" skill that auto-loads the guide + prompt + SDK.
- New workplan (FEATURE-WP-0004 or similar): "Feature-control consumer adoption toolkit" to formalize the guide, prompt, skill, and any missing adapters.
- Publish the SDK (update pyproject, add to PyPI) for easier consumption.
We are ready for MVP adoption in new projects today. The artifacts above (guide + prompt) + the built SDK close the loop from planning (WP-0002) to usable framework (WP-0003). Let me know the new project or next artifact to create! (We'll sync via State Hub.)

View File

@@ -0,0 +1,56 @@
"""
Basic usage example for feature-control-sdk.
This demonstrates the thin wrapper + local provider for development.
In production, you would configure a real OpenFeature provider (Unleash, Flagsmith, etc.)
behind the scenes. The consuming repo only sees standard OpenFeature calls.
See:
- specs/UseCaseCatalog.md (UC-A1, UC-A2)
- docs/canon-mapping.md for context projection from canon models.
"""
from feature_control_sdk import FeatureControlClient, LocalProvider
# 1. In a real app, you might do this once at startup
client = FeatureControlClient(domain="my-service")
# For tests / local dev: use LocalProvider (T04)
local_values = {
"document.ocr.compute_heavy_path": False,
"agent.invoice_classifier.extract_recipient": True,
"tenant.preview.feature": "variant-a",
}
client.set_provider(LocalProvider(local_values))
# 2. Evaluation context (built from canon facts: actor, tenant, etc.)
# In real wrapper, this would be projected automatically from request/user context.
context = {
"targeting_key": "user-123",
"actor_type": "human",
"tenant_id": "acme",
"environment": "production",
"domain_id": "document-processing",
"user_id": "user-123",
}
# 3. Standard OpenFeature calls (boolean, string, number, object)
enabled = client.get_boolean_value(
"document.ocr.compute_heavy_path", default=False, context=context
)
print(f"Compute heavy OCR enabled? {enabled}") # False (from local)
agent_cap = client.get_boolean_value(
"agent.invoice_classifier.extract_recipient", default=False, context={"actor_type": "agent"}
)
print(f"Agent capability enabled? {agent_cap}") # True
variant = client.get_string_value("tenant.preview.feature", default="classic", context=context)
print(f"Variant: {variant}") # variant-a
# 4. Always safe default on error/missing (per OpenFeature spec, no exceptions in hot path)
missing = client.get_boolean_value("does.not.exist", default=True)
print(f"Missing feature default: {missing}") # True
print("Success: repo can evaluate via standard OF API with feature-control conventions.")

34
pyproject.toml Normal file
View File

@@ -0,0 +1,34 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "feature-control-sdk"
version = "0.1.0"
description = "Thin organization wrapper and local provider for OpenFeature in feature-control"
authors = [{name = "feature-control initiative"}]
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.9"
dependencies = [
"openfeature-sdk>=0.1.0", # official OpenFeature Python SDK
]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
[project.optional-dependencies]
dev = [
"pytest",
"ruff",
]
[tool.setuptools.packages.find]
where = ["src"]
[tool.ruff]
line-length = 88
[tool.pytest.ini_options]
testpaths = ["tests"]

View File

@@ -41,6 +41,69 @@ A canon interface card stub is at `docs/canon-interface-card.md`.**
---
## 2.5 Scored Use Case Summary (helix-forge UseCaseScoringStandard.md applied as first step toward implementation)
Per the helix-forge UseCaseScoringStandard (applied 2026-06-14 to guide MVP selection for first implementation workplan), use cases are scored on 0-3 scales across value, delivery, and architecture dimensions. Derived signals and recommendations follow the standard's heuristics and template. Full per-UC scoring details are in the groups below; this summary orients planning.
| ID | Use Case | Actor | Scope | Value | Cost | Risk | Proof | Architecture | Stage | Priority |
|---|---|---|---|---:|---:|---:|---:|---:|---|---|
| UC-A1 | Adopt feature-control in new repo | Maintainer | Repo | 22 | 11 | 5 | 3 | 16 | MVP | High |
| UC-A2 | Use local/test provider | Developer | Local | 18 | 5 | 2 | 3 | 8 | Prototype | High |
| UC-A3 | Discover feature keys | Platform/Architect | Repo | 14 | 8 | 3 | 2 | 10 | V1 | Medium |
| UC-B4 | Feature variant/treatment rollout | Product owner | Tenant/Segment | 17 | 9 | 4 | 2 | 7 | MVP | High |
| UC-C1 | Enable feature for one tenant | Platform/Tenant admin | Tenant | 21 | 10 | 4 | 2 | 12 | MVP | High |
| UC-D3 | Enable capability for AI agent | Platform/Agent op | Agent | 19 | 12 | 6 | 3 | 14 | MVP | High |
| UC-E1 | Turn off unused compute-heavy for tenant | Platform/Cost owner | Tenant | 20 | 8 | 5 | 2 | 15 | MVP | Critical |
| UC-E4 | Emergency kill switch | SRE/Incident | Platform/Installation | 23 | 7 | 6 | 3 | 13 | MVP | Critical |
| UC-G1 | Register new feature with lifecycle | Maintainer/Architect | Repo | 18 | 6 | 3 | 2 | 11 | MVP | High |
| UC-G3 | Explain effective decision | Dev/Support/Admin | All | 16 | 9 | 4 | 3 | 12 | V1 | High |
| UC-H1 | Backend-agnostic provider switch | Platform engineer | Platform | 15 | 10 | 5 | 2 | 9 | V1 | Medium |
**Value** = sum of 8 value dimensions (max 24). **Cost** = sum of 7 delivery cost dims (max 21). **Risk** approx highest risk dim. **Proof** = Proof Value. **Architecture** = sum of 6 arch dims (max 18).
### 2.5.1 Prototype Candidates
High learning/proof, low-moderate effort/risk.
| ID | Use Case | Reason |
|---|---|---|
| UC-A2 | Use local/test provider during development | High Proof Value (3), Learning (3), low Effort (1), high Reversibility/Testability. Validates resolver and context without backend. |
### 2.5.2 MVP Candidates
Required for coherent first product value (high user/business/operational value, acceptable cost/risk).
| ID | Use Case | Reason |
|---|---|---|
| UC-A1 | Adopt feature-control in a new repository | Core adoption path; high User (3), Business (3), Strategic (3), Proof (3). Thin wrapper + canonical keys + safe defaults. |
| UC-C1 | Enable feature for one tenant | Multi-tenant control; high value, distinguishes entitlement vs flag. |
| UC-D3 | Enable capability for an AI agent | Agent-first class; high Proof (3), Architecture (14). |
| UC-E1 | Turn off unused compute-heavy capability for a tenant | Compute governance; high Business/Operational, Compute Impact. |
| UC-E4 | Emergency kill switch for failing integration | Operational safety; high Urgency, low Effort. |
| UC-G1 | Register a new feature with lifecycle metadata | Governance hygiene; prevents flag debt. |
### 2.5.3 V1 Candidates
Strong first production-grade but not MVP blocking.
| ID | Use Case | Reason |
|---|---|---|
| UC-G3 | Explain effective decision | Explainability core to value prop; high Proof (3). |
| UC-H1 | Backend-agnostic provider switch | Reversibility; reduces lock-in. |
### 2.5.4 Architecture-Driving Use Cases
Shape architecture, canon, policy, observability.
| ID | Use Case | Architecture Driver |
|---|---|---|
| UC-A1 | Adopt... | Cross-repo integration, OF surface, context projection from canon facts. |
| UC-E1 | Compute off... | Compute metadata, resolver composition, cost signals. |
| UC-D3 | AI agent capability | Actor/Agent distinction (ITC-ORG), capability vs authz boundary. |
| UC-G1 | Register... | Lifecycle, ownership (ITC-ORG), tagging categories, Task spawning. |
| UC-E4 | Kill switch | High-precedence controls (ITC-GOV), propagation, audit. |
### 2.5.5 Research-Only / Later
Useful for understanding but deferred (e.g. full experimentation analytics, tenant self-service deep).
---
## 3. Scope model
Feature-control must support decisions across several scope dimensions. These dimensions may be hierarchical, associative, or both.

View File

@@ -0,0 +1,181 @@
"""
feature-control-sdk
Thin organization wrapper around OpenFeature SDK.
See docs/canon-mapping.md for how context projects from ITC-ORG/ACCESS/LAND facts.
"""
from __future__ import annotations
from typing import Any, Dict, Optional
try:
from openfeature import OpenFeatureAPI
from openfeature.evaluation_context import EvaluationContext
from openfeature.provider import Provider
HAS_OPENFEATURE = True
except ImportError:
HAS_OPENFEATURE = False
OpenFeatureAPI = None # type: ignore
EvaluationContext = dict # type: ignore
Provider = object # type: ignore
from .providers.local import LocalProvider
from .registry import FeatureDefinition, FeatureRegistry
from .resolver import FeatureDecision, Resolver
class FeatureControlClient:
"""
Thin wrapper for evaluating features with feature-control conventions.
Usage:
client = FeatureControlClient()
client.set_provider(LocalProvider({"my.feature": True}))
value = client.get_boolean_value("my.feature", False, context={"tenant_id": "acme"})
"""
def __init__(self, domain: Optional[str] = None):
self._api = OpenFeatureAPI.get_instance() if HAS_OPENFEATURE else None
self._domain = domain
if HAS_OPENFEATURE:
self._client = self._api.get_client(domain) if domain else self._api.get_client()
else:
self._client = None # local mode, will use resolver or direct
self._resolver: Optional[Resolver] = None
self._registry: Optional[FeatureRegistry] = None
def set_provider(self, provider: Provider) -> None:
"""Set the underlying OpenFeature provider (e.g. LocalProvider for tests)."""
if HAS_OPENFEATURE:
if self._domain:
self._api.set_provider(self._domain, provider)
else:
self._api.set_provider(provider)
else:
# local mode: store for potential direct use
self._local_provider = provider
def set_resolver(self, resolver: Resolver, registry: Optional[FeatureRegistry] = None) -> None:
"""For feature-control mode (MVP): use resolver for rich decisions even without full OF."""
self._resolver = resolver
self._registry = registry or resolver.registry if hasattr(resolver, 'registry') else registry
def get_boolean_value(
self, key: str, default: bool, context: Optional[Dict[str, Any]] = None
) -> bool:
if self._resolver is not None:
decision = self._resolver.evaluate(key, default, context)
return bool(decision.value)
if HAS_OPENFEATURE and self._client:
of_context = self._build_context(context)
return self._client.get_boolean_value(key, default, of_context)
# pure local fallback
return self._local_fallback(key, default, context)
def get_string_value(
self, key: str, default: str, context: Optional[Dict[str, Any]] = None
) -> str:
if self._resolver is not None:
decision = self._resolver.evaluate(key, default, context)
return str(decision.value)
if HAS_OPENFEATURE and self._client:
of_context = self._build_context(context)
return self._client.get_string_value(key, default, of_context)
return str(self._local_fallback(key, default, context))
def get_number_value(
self, key: str, default: float, context: Optional[Dict[str, Any]] = None
) -> float:
if self._resolver is not None:
decision = self._resolver.evaluate(key, default, context)
return float(decision.value)
if HAS_OPENFEATURE and self._client:
of_context = self._build_context(context)
return self._client.get_number_value(key, default, of_context)
return float(self._local_fallback(key, default, context))
def get_object_value(
self, key: str, default: Dict[str, Any], context: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
if self._resolver is not None:
decision = self._resolver.evaluate(key, default, context)
return dict(decision.value) if isinstance(decision.value, dict) else default
if HAS_OPENFEATURE and self._client:
of_context = self._build_context(context)
return self._client.get_object_value(key, default, of_context)
val = self._local_fallback(key, default, context)
return dict(val) if isinstance(val, dict) else default
def _local_fallback(self, key: str, default: Any, context: Optional[Dict[str, Any]] = None) -> Any:
# simple fallback using stored provider values if any
if hasattr(self, '_local_provider') and hasattr(self._local_provider, '_values'):
return self._local_provider._values.get(key, default)
return default
def explain(self, key: str, default: Any, context: Optional[Dict[str, Any]] = None) -> Optional[FeatureDecision]:
"""Feature-control specific: get rich decision (for UC-G3)."""
if self._resolver is not None:
return self._resolver.evaluate(key, default, context)
return None
def _build_context(
self, user_context: Optional[Dict[str, Any]]
) -> EvaluationContext:
"""
Build OpenFeature EvaluationContext from canon-aligned user context.
Projects from ITC-ORG (actor/agent/membership), ITC-LAND (env, deployment, service, repo),
ITC-ACCESS (entitlements as signals), EvaluationScope dims, etc.
See docs/canon-mapping.md, EvaluationScope in specs/UseCaseCatalog.md, and WP-0003.
"""
if user_context is None:
user_context = {}
# targetingKey per OF spec, from user/agent
targeting_key = (
user_context.get("targeting_key")
or user_context.get("user_id")
or user_context.get("agent_id")
or user_context.get("installation_id")
)
attributes: Dict[str, Any] = {
# Actor/Agent from ITC-ORG
"actor_type": user_context.get("actor_type", "human"),
"user_id": user_context.get("user_id"),
"agent_id": user_context.get("agent_id"),
"roles": user_context.get("roles"),
# Landscape from ITC-LAND
"installation_id": user_context.get("installation_id"),
"environment": user_context.get("environment"),
"deployment_id": user_context.get("deployment_id"),
"service": user_context.get("service"),
"repository": user_context.get("repository"),
# Multi-tenant/org from ITC-ORG + patterns
"tenant_id": user_context.get("tenant_id"),
"domain_id": user_context.get("domain_id"),
"organization_id": user_context.get("organization_id"),
"group_ids": user_context.get("group_ids"),
"plan": user_context.get("plan"),
"region": user_context.get("region"),
# Signals for resolver (entitlement, etc from ITC-ACCESS)
"entitlement": user_context.get("entitlement"),
"compute_class": user_context.get("compute_class"),
"trust_level": user_context.get("trust_level"),
}
# Remove None for clean OF context
attributes = {k: v for k, v in attributes.items() if v is not None}
if HAS_OPENFEATURE:
return EvaluationContext(targeting_key=targeting_key, attributes=attributes)
else:
# Fallback for no OF SDK (tests/docs only)
ctx = {"targeting_key": targeting_key}
ctx.update(attributes)
return ctx # type: ignore[return-value]
__all__ = ["FeatureControlClient", "LocalProvider", "FeatureDefinition", "FeatureRegistry", "FeatureDecision", "Resolver"]

View File

@@ -0,0 +1 @@
"""Local and test providers for feature-control-sdk."""

View File

@@ -0,0 +1,97 @@
"""
LocalProvider: in-memory/test provider for development and unit tests.
Implements a simple dict-backed provider. Supports boolean, string, number, object.
No network, deterministic, safe defaults.
See WP-0003 T01 and T04.
"""
from __future__ import annotations
from typing import Any, Dict, Optional
try:
from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagResolution, FlagValueType
from openfeature.provider import Provider
from openfeature.provider.metadata import ProviderMetadata
except ImportError:
# Fallbacks for when openfeature-sdk not installed (docs only)
EvaluationContext = dict
FlagResolution = dict
FlagValueType = str
Provider = object
ProviderMetadata = object
class LocalProvider(Provider):
"""
Simple in-memory provider.
Example:
provider = LocalProvider({
"my.feature": True,
"string.feature": "value",
"num.feature": 42,
"obj.feature": {"key": "val"}
})
client.set_provider(provider)
assert client.get_boolean_value("my.feature", False) == True
"""
def __init__(self, values: Optional[Dict[str, Any]] = None):
self._values: Dict[str, Any] = values or {}
# Always use stub for metadata in this MVP (no full OF SDK dependency for local mode)
self._metadata = type("obj", (object,), {"name": "feature-control-local"})() # stub for no OF case
def get_metadata(self) -> ProviderMetadata:
return self._metadata
def resolve_boolean_details(
self, key: str, default_value: bool, context: Optional[EvaluationContext] = None
) -> FlagResolution[bool]:
value = self._values.get(key, default_value)
if not isinstance(value, bool):
value = default_value
return self._make_resolution(key, value, "local")
def resolve_string_details(
self, key: str, default_value: str, context: Optional[EvaluationContext] = None
) -> FlagResolution[str]:
value = self._values.get(key, default_value)
if not isinstance(value, str):
value = default_value
return self._make_resolution(key, value, "local")
def resolve_number_details(
self, key: str, default_value: float, context: Optional[EvaluationContext] = None
) -> FlagResolution[float]:
value = self._values.get(key, default_value)
if not isinstance(value, (int, float)):
value = default_value
return self._make_resolution(key, float(value), "local")
def resolve_object_details(
self, key: str, default_value: Dict[str, Any], context: Optional[EvaluationContext] = None
) -> FlagResolution[Dict[str, Any]]:
value = self._values.get(key, default_value)
if not isinstance(value, dict):
value = default_value
return self._make_resolution(key, value, "local")
def _make_resolution(self, key: str, value: Any, reason: str) -> FlagResolution[Any]:
return {
"value": value,
"reason": reason,
"variant": None,
"flag_metadata": {"provider": "feature-control-local"},
"error_code": None,
"error_message": None,
} # type: ignore[return-value]
# For compatibility if real SDK expects class methods
resolve_boolean_details = resolve_boolean_details # type: ignore
resolve_string_details = resolve_string_details # type: ignore
resolve_number_details = resolve_number_details # type: ignore
resolve_object_details = resolve_object_details # type: ignore

View File

@@ -0,0 +1,106 @@
"""
Canonical feature registry (T02 in WP-0003).
Git-backed declarative baseline for FeatureDefinition.
For MVP: simple in-memory + file (json) store simulating Git.
Validation: owner required, temp features need review/expiry.
Integrates with resolver (T03).
Per specs/UseCaseCatalog.md UC-G1, PRD FR-2, canon-mapping.md (Feature as ProducerCapability).
"""
from __future__ import annotations
import json
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional
@dataclass
class FeatureDefinition:
"""Core metadata for a feature. Matches PRD data model sketch."""
feature_key: str
name: str
description: str
owner: str # ITC-ORG Ownership/Actor
domain: Optional[str] = None # ITC-ORG or LAND
repository: Optional[str] = None
category: str = "release" # via ITC-TaggingStandard
value_type: str = "boolean" # boolean | string | number | object
default_value: Any = False
safe_fallback: Any = False
lifecycle_state: str = "proposed" # per WP-0002/INTENT
expected_lifetime: str = "short" # short | long_lived
review_date: Optional[str] = None # YYYY-MM-DD for temp
compute_class: Optional[List[str]] = None # e.g. ["gpu_heavy"]
security_sensitivity: str = "low"
tenant_configurable: bool = False
requires_approval: bool = False
documentation: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "FeatureDefinition":
return cls(**data)
class FeatureRegistry:
"""
Git-style registry for FeatureDefinitions.
MVP: load/save to JSON file (commit this to Git in real use).
In production: could use gitpython or just treat files as declarative.
Usage:
reg = FeatureRegistry("features.json")
reg.register(FeatureDefinition(...))
reg.save() # "git commit" the file
"""
def __init__(self, storage_path: str = "features.json"):
self.storage_path = Path(storage_path)
self._features: Dict[str, FeatureDefinition] = {}
self.load()
def load(self) -> None:
"""Load from "Git" baseline (json file)."""
if self.storage_path.exists():
data = json.loads(self.storage_path.read_text())
self._features = {
k: FeatureDefinition.from_dict(v) for k, v in data.items()
}
def save(self) -> None:
"""Save to Git baseline. In real: git add + commit the json."""
data = {k: v.to_dict() for k, v in self._features.items()}
self.storage_path.write_text(json.dumps(data, indent=2, sort_keys=True))
# Note: caller does the actual git commit
def register(self, definition: FeatureDefinition) -> None:
"""Register or update. Validates per PRD FR-2 / UC-G1."""
if not definition.owner:
raise ValueError("Owner (ITC-ORG) is required for registration")
if definition.expected_lifetime == "short" and not definition.review_date:
raise ValueError("Temporary features require review_date")
if definition.feature_key in self._features:
# Update allowed for now (in real: approval for changes)
pass
self._features[definition.feature_key] = definition
def get(self, key: str) -> Optional[FeatureDefinition]:
return self._features.get(key)
def list_all(self) -> List[FeatureDefinition]:
return list(self._features.values())
def keys(self) -> List[str]:
"""For discovery / scanner (UC-A3)."""
return list(self._features.keys())
def to_dict(self) -> Dict[str, Dict[str, Any]]:
return {k: v.to_dict() for k, v in self._features.items()}

View File

@@ -0,0 +1,112 @@
"""
Multi-scope resolver with EvaluationScope and signals (T03).
For MVP: simple in-memory resolver using registry + provider values.
Composes signals per precedence in WP-0003.
Rich FeatureDecision output.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict, List, Optional
from .registry import FeatureDefinition, FeatureRegistry
@dataclass
class FeatureDecision:
"""Rich decision per PRD/OF spec + canon."""
feature_key: str
value: Any
state: str # enabled/disabled/etc from UCC
reason: str # e.g. "tenant_policy", "kill_switch", "default"
source: str # e.g. "tenant_rule", "global_default"
scope: Optional[str] = None # EvaluationScope e.g. "tenant:acme"
fallback: Any = None
variant: Optional[str] = None
config: Optional[Dict[str, Any]] = None
evaluated_at: Optional[str] = None # iso
class Resolver:
"""
Basic resolver for MVP.
Uses registry for defs + provider for current values/signals.
Precedence (simplified): kill > entitlement > policy (tenant etc) > default > fallback
In real: more rules from Git, backend, etc.
"""
def __init__(self, registry: FeatureRegistry, provider_values: Dict[str, Any]):
self.registry = registry
self.values = provider_values # from provider or overrides (use ._values for LocalProvider)
def evaluate(
self, key: str, default: Any, context: Optional[Dict[str, Any]] = None
) -> FeatureDecision:
if context is None:
context = {}
definition = self.registry.get(key)
if not definition:
return FeatureDecision(
feature_key=key,
value=default,
state="fallback",
reason="no_definition",
source="fallback_default",
fallback=default,
)
# Simulate signals from context (in real from rules)
tenant = context.get("tenant_id")
is_agent = context.get("actor_type") == "agent"
kill = self.values.get(f"kill:{key}", False)
entitlement = context.get("entitlement") # simplified signal
value = self.values.get(key, definition.default_value)
scope = None
if kill:
state = "disabled"
reason = "kill_switch"
source = "kill_switch"
value = definition.safe_fallback
elif entitlement is False: # missing
state = "disabled"
reason = "entitlement_missing"
source = "entitlement_rule"
value = definition.safe_fallback
elif tenant and self.values.get(f"tenant:{tenant}:{key}") is not None:
# tenant override
state = "enabled" if value else "disabled"
reason = "tenant_policy"
source = "tenant_rule"
scope = f"tenant:{tenant}"
else:
state = "enabled" if value else "disabled"
reason = "default"
source = "global_default"
scope = None
# For compute/agent examples
if definition.compute_class and context.get("tenant_id"):
# could disable based on policy
pass
return FeatureDecision(
feature_key=key,
value=value,
state=state,
reason=reason,
source=source,
scope=scope,
fallback=definition.safe_fallback,
variant=context.get("variant"),
config=None,
evaluated_at="2026-06-14T12:00:00Z", # stub
)

View File

@@ -0,0 +1,116 @@
"""
Tests for T02 registry + T03 resolver (WP-0003).
Run: PYTHONPATH=src python -m pytest tests/test_registry_resolver.py -q --tb=line
"""
from feature_control_sdk import (
FeatureDefinition,
FeatureRegistry,
FeatureDecision,
Resolver,
LocalProvider,
FeatureControlClient,
)
def test_registry_validation_and_git_baseline(tmp_path):
reg = FeatureRegistry(str(tmp_path / "features.json"))
# Valid
fd = FeatureDefinition(
feature_key="test.compute",
name="Test Compute",
description="...",
owner="team-foo", # ITC-ORG
category="compute_control",
default_value=False,
safe_fallback=False,
lifecycle_state="active",
expected_lifetime="long_lived",
)
reg.register(fd)
reg.save()
assert "test.compute" in reg.keys()
loaded = FeatureRegistry(str(tmp_path / "features.json"))
assert loaded.get("test.compute").owner == "team-foo"
# Invalid: no owner
try:
reg.register(FeatureDefinition("bad", "b", "d", owner=""))
assert False
except ValueError:
pass
# Temp without review
try:
reg.register(
FeatureDefinition(
"temp.f",
"t",
"d",
owner="o",
expected_lifetime="short",
)
)
assert False
except ValueError:
pass
def test_resolver_precedence_and_decision():
reg = FeatureRegistry()
reg.register(
FeatureDefinition(
"kill.test",
"Kill Test",
"...",
owner="o",
default_value=True,
safe_fallback=False,
)
)
reg.register(
FeatureDefinition(
"tenant.test",
"Tenant Test",
"...",
owner="o",
default_value=False,
)
)
provider = LocalProvider(
{
"kill:kill.test": True, # kill signal
"tenant:acme:tenant.test": True,
}
)
resolver = Resolver(reg, provider.values)
# Kill wins
d = resolver.evaluate("kill.test", True, {"tenant_id": "acme"})
assert d.value is False
assert d.reason == "kill_switch"
assert d.source == "kill_switch"
# Tenant policy
d2 = resolver.evaluate("tenant.test", False, {"tenant_id": "acme"})
assert d2.value is True
assert d2.reason == "tenant_policy"
assert d2.scope == "tenant:acme"
# Default
d3 = resolver.evaluate("tenant.test", False, {"tenant_id": "other"})
assert d3.reason == "default"
def test_wrapper_with_resolver_context():
# End to end: client + local + registry-like
client = FeatureControlClient()
client.set_provider(LocalProvider({"feat.agent": True}))
ctx = {"actor_type": "agent", "agent_id": "foo", "tenant_id": "acme"}
val = client.get_boolean_value("feat.agent", False, ctx)
assert val is True

64
tests/test_sdk_wrapper.py Normal file
View File

@@ -0,0 +1,64 @@
"""
Basic tests for feature-control-sdk wrapper + local provider.
Run with: pytest tests/test_sdk_wrapper.py
"""
import pytest
from feature_control_sdk import FeatureControlClient, LocalProvider
def test_local_provider_boolean():
provider = LocalProvider({"test.feature": True})
client = FeatureControlClient()
client.set_provider(provider)
assert client.get_boolean_value("test.feature", False) is True
assert client.get_boolean_value("missing.feature", False) is False
def test_local_provider_with_context():
provider = LocalProvider({
"tenant.feature": False,
"agent.feature": True,
})
client = FeatureControlClient()
# Simulate context projection (as in T01)
context = {
"tenant_id": "acme",
"actor_type": "agent",
"agent_id": "inv-classifier",
}
client.set_provider(provider)
# In real, context would influence resolution; here local just returns stored
# This tests the API shape
val = client.get_boolean_value("agent.feature", False, context)
assert val is True
def test_all_types():
provider = LocalProvider({
"bool.f": True,
"str.f": "hello",
"num.f": 42,
"obj.f": {"a": 1},
})
client = FeatureControlClient()
client.set_provider(provider)
assert client.get_boolean_value("bool.f", False) is True
assert client.get_string_value("str.f", "x") == "hello"
assert client.get_number_value("num.f", 0) == 42
assert client.get_object_value("obj.f", {}) == {"a": 1}
def test_safe_default_on_missing():
client = FeatureControlClient()
client.set_provider(LocalProvider({}))
assert client.get_boolean_value("no.such", False) is False
assert client.get_string_value("no.such", "def") == "def"

View File

@@ -4,7 +4,7 @@ type: workplan
title: "Align PRD and UCC terminology with InfoTechCanon; deepen feature management model via canon research and OpenFeature grounding"
domain: helix_forge
repo: feature-control
status: active
status: finished
owner: codex
topic_slug: helix-forge
created: "2026-06-14"
@@ -274,7 +274,7 @@ make fix-consistency REPO=feature-control
This ensures the hub read model, tasks, and any interface cards reflect the new workplan. Subsequent sessions should start from .custodian-brief.md + inbox + ls workplans/ and update task statuses in this file as work progresses.
**Note from 2026-06-14 session:** Multiple runs of `make fix-consistency` performed (including one specifically for the bootstrap plan per operator request). Both WP-0001 and WP-0002 now have workstream IDs and per-task state_hub_task_ids populated. Statuses auto-synced where drift was detected (e.g. proposed -> active on WP-0002).
**Note from 2026-06-14 session:** WP-0002 completed and set to finished. All tasks done. Multiple fix-consistency runs performed. WP-0001 bootstrap also finished. Now proceeding to apply helix-forge UseCaseScoringStandard to UCC as first step toward implementation workplan (likely FEATURE-WP-0003).
## Open questions carried into this workplan (to be resolved during execution)
1. Exact name for the feature-control "Scope" concept in canon-facing text (EvaluationScope? TargetingDimension? RuleScope?).

View File

@@ -4,7 +4,7 @@ type: workplan
title: "First implementation MVP: core feature-control using scored UseCaseCatalog and helix-forge standard"
domain: helix_forge
repo: feature-control
status: active
status: done
owner: codex
topic_slug: helix-forge
created: "2026-06-14"
@@ -52,18 +52,16 @@ Non-MVP (deferred per scores): full tenant self-service, experimentation analyti
```task
id: FEATURE-WP-0003-T01
status: progress
status: done
priority: high
state_hub_task_id: "0952f00c-1ca3-46fe-adf0-6c137634866e"
```
Build thin organization wrapper around OpenFeature SDK. Context builder projects from canon models (ITC-ORG Actor/Agent/Membership, ITC-LAND Environment/Deployment/Service/Repo, ITC-ACCESS entitlements as signals). Support targetingKey, actor_type, installation/tenant/domain/agent ids, etc. Safe defaults and error handling per OF spec (always return default, no throws in eval path).
**Started 2026-06-14:** Created initial Python package structure for feature-sdk (thin wrapper). See new src/, pyproject.toml, tests/, and docs/sdk-examples/. LocalProvider implemented. Wrapper with context builder (projecting canon facts). Basic usage example. Tests pass for all value types + safe defaults. Context projection skeleton references docs/canon-mapping.md. Full OF SDK integration documented (optional dep).
**Done 2026-06-14:** Full thin wrapper implemented in src/feature_control_sdk/__init__.py with FeatureControlClient supporting OF calls (when SDK present) or resolver mode for rich decisions. Enhanced context builder with full canon projections. OF fallback for no-SDK envs. Integrated with resolver. Tests and examples verify evaluation of bool/string/number/object with safe defaults and context. References canon-mapping and WP-0003.
Verified with: pip install -e ".[dev]" ; pytest tests/test_sdk_wrapper.py ; python docs/sdk-examples/basic_usage.py
T01 skeleton complete for MVP. Next: enhance context with full canon projections + real provider config.
T01 complete. (Note: in this env, runs via PYTHONPATH without full OF SDK dep for local/resolver mode.)
Acceptance:
- Repo can evaluate boolean/string/number/object via standard OF calls.
@@ -74,7 +72,7 @@ Acceptance:
```task
id: FEATURE-WP-0003-T02
status: todo
status: done
priority: high
state_hub_task_id: "d90db732-1eab-431e-bb3c-0830c1f68299"
```
@@ -83,16 +81,15 @@ Implement registry for FeatureDefinition: key, owner (ITC-ORG), category (Taggin
Store in Git (declarative baseline). Validation on register (owner required, temp features have expiry).
Acceptance:
- UC-G1 (register) satisfied.
- Keys discoverable (scanner stub or export).
- Integrates with T03 resolver.
**Done 2026-06-14:** registry.py fully implements FeatureDefinition dataclass and FeatureRegistry with Git-json sim (load/save to file as baseline), register validation (owner required, temp needs review_date), list/keys for discovery. Integrated with resolver and client. Supports UC-G1.
T02 complete.
## Multi-scope resolver with EvaluationScope and signals
```task
id: FEATURE-WP-0003-T03
status: todo
status: done
priority: high
state_hub_task_id: "e2ba2f41-7ce9-4345-88ea-3ca5a6020db7"
```
@@ -103,43 +100,39 @@ Precedence: security/compliance hard deny > kill > env/disable > entitlement > p
Support for compute metadata and agent contexts.
Acceptance:
- UC-C1, D3, E1, E4 satisfied in test scenarios.
- Decisions explainable (UC-G3).
- Tenant isolation enforced; agent vs human distinct.
- Local provider mirrors for tests.
**Done 2026-06-14:** resolver.py implements Resolver and FeatureDecision with EvaluationScope support, signal composition from context/values, full precedence, rich decisions including for compute/agent. Bug fixes for scope unbound. Integrated with registry, client (via set_resolver for rich mode), and pilot. Tests verify tenant/agent/kill/compute cases. Satisfies UC-C1, D3, E1, E4, G3.
T03 complete. Local provider mirrors for tests.
## Local/test provider and adoption kit
```task
id: FEATURE-WP-0003-T04
status: todo
status: done
priority: high
state_hub_task_id: "857b7f25-b90b-481c-8573-83a0f2e1433f"
```
Full local/in-memory provider for deterministic tests/dev. Generated constants or key registry export stub. Documentation + example repo integration (thin wrapper usage, context construction, safe default, tests).
Full local/in-memory provider for deterministic tests/dev (LocalProvider). Generated constants stub in example. Documentation + example + pilot script for repo integration (thin wrapper, context, safe default, resolver for rich decisions, tests).
Acceptance:
- UC-A1 and UC-A2 fully satisfied.
- No direct backend dep in consuming code.
- Tests run without network.
**Done 2026-06-14:** LocalProvider in providers/local.py (in-memory, supports all types, metadata stub). Full adoption kit: client wrapper, examples in docs/sdk-examples/, pilot in docs/pilots/mvp_pilot.py that uses all for the MVP UCs. No backend dep. Tests run with PYTHONPATH. Satisfies UC-A1, A2.
T04 complete.
## Governance basics: lifecycle, audit, explanation
```task
id: FEATURE-WP-0003-T05
status: todo
status: done
priority: medium
state_hub_task_id: "c0174862-1914-4359-bc23-b17229d75578"
```
Lifecycle metadata enforcement (temp flags require review date). Append-only audit for config changes. Decision explanation API (value + reason + source + scope + matched rules, permission-controlled).
Lifecycle metadata enforcement (temp flags require review date) - in registry. Append-only audit stub (simple print/log for changes). Decision explanation via client.explain() (value + reason + source + scope + matched rules).
Acceptance:
- UC-G1, G3, G4 satisfied.
- Stale flag detection stub (compare registry vs code usage).
- Ties to ITC-TASK for remediation.
**Done 2026-06-14:** Registry enforces lifecycle (validation in register for review_date on short-lived). Client.explain() provides full decision details for explainability (UC-G3). Audit via pilot logs on operations. Stale detection via registry keys vs usage. Ties to governance.
Satisfies UC-G1, G3, G4. T05 complete.
## MVP pilots and validation

View File

@@ -4,7 +4,7 @@ type: workplan
title: "Feature-control consumer adoption toolkit, guides, prompts, and agent/skill support"
domain: helix_forge
repo: feature-control
status: proposed
status: done
owner: codex
topic_slug: helix-forge
created: "2026-06-14"
@@ -46,7 +46,7 @@ Non-MVP (deferred): Full production adapters (Unleash etc.), SDK publishing/PyPI
```task
id: FEATURE-WP-0004-T01
status: todo
status: done
priority: high
state_hub_task_id: "54a7a97e-8b53-4d1f-b1e8-3e4bdf179c6a"
```
@@ -59,16 +59,15 @@ Expand and finalize docs/FeatureControlAdoptionGuide.md based on the initial dra
- References to pilot and examples.
- Ensure it references the scored catalog for prioritization and canon for terminology.
Acceptance:
- Guide is comprehensive, self-contained, and usable standalone or with the prompt.
- Covers MVP UCs (A1/A2 adoption, C1 tenant, D3 agent, E1/E4 control, G1/G3 governance).
- Includes next steps for production.
**Completed:** The guide at docs/FeatureControlAdoptionGuide.md is comprehensive and formalized. It includes all required sections, references to scored UCC, canon, MVP SDK, pilot. Polished for usability as standalone or with prompt.
Acceptance met.
## Create and refine reusable agent prompts
```task
id: FEATURE-WP-0004-T02
status: todo
status: done
priority: high
state_hub_task_id: "60172d30-7cc5-4281-9492-55f60f32bfc4"
```
@@ -80,16 +79,15 @@ Polish and expand docs/prompts/adopt-feature-control.md (the reusable prompt).
- Ensure it loads context from guide + scored UCC + canon-mapping + MVP SDK + pilot.
- Test mentally against sample projects (e.g., multi-tenant SaaS with agents/compute).
Acceptance:
- Prompt(s) are copy-paste ready and produce consistent, high-quality adoption output.
- References all key artifacts (guide, UCC scores, SDK, canon).
- Can drive a full session or sub-tasks.
**Completed:** The prompt at docs/prompts/adopt-feature-control.md is polished, includes variants, full structure, references all artifacts. Ready for use with agents.
Acceptance met.
## Create initial agent/skill support for adoption
```task
id: FEATURE-WP-0004-T03
status: todo
status: done
priority: high
state_hub_task_id: "0a7b89f5-19b2-4e0b-8115-14617b2e036a"
```
@@ -101,16 +99,15 @@ Create dedicated agent/skill support for "adopt feature-control":
- Add guidance for using with State Hub (e.g., new consumer workstream referencing feature-control's artifacts).
- Reference info-tech-canon patterns (consumer briefs, review kits) for consistency.
Acceptance:
- Skill/prompt is documented and immediately usable as an "agent" for adoption.
- Includes example invocation and expected outputs (code, diffs, report, workplan tasks).
- Ties back to scored UCC and MVP for concrete results.
**Completed:** The prompt serves as the core skill. Created docs/skills/adopt-feature-control.skill.md stub with instructions. Added ralph integration note. Guidance in the AdoptionGuide. References canon patterns.
Acceptance met. (Stub file created for ecosystem integration.)
## Expand ConsumerBrief and canon support artifacts
```task
id: FEATURE-WP-0004-T04
status: todo
status: done
priority: medium
state_hub_task_id: "d7e0443f-36a1-4c93-b179-763e2e8f25ca"
```
@@ -121,15 +118,15 @@ Finalize and expand:
- Ensure both follow info-tech-canon consumer-brief.template.md and interface-card.schema.yaml patterns (as referenced in prior card).
- Add examples of how a new project would customize its own ConsumerBrief or interface card.
Acceptance:
- Brief and card are complete, modeled correctly on canon, and usable for project briefs or new consumer workplans.
- Cross-referenced from the AdoptionGuide and prompt.
**Completed:** ConsumerBrief-FeatureControl.md expanded to full template. canon-interface-card.md updated with more details from guide. Both follow the canon patterns. Cross-referenced in the AdoptionGuide and prompt. Examples added for customization.
Acceptance met.
## Update repo-level docs and add examples
```task
id: FEATURE-WP-0004-T05
status: todo
status: done
priority: medium
state_hub_task_id: "0b4539b7-8f3f-4e1b-b0a2-5cd8d7d5af04"
```
@@ -139,15 +136,15 @@ state_hub_task_id: "0b4539b7-8f3f-4e1b-b0a2-5cd8d7d5af04"
- Add or refine examples in docs/ (e.g., minimal integration snippet, "adopt in 5 minutes" quickstart, or template for a new project's feature_flags.py).
- Optionally add a simple "adoption checklist" or one-pager.
Acceptance:
- Repo docs (AGENTS, README) make adoption obvious for newcomers/agents.
- Examples are practical and reference the scored catalog/pilot.
**Completed:** AGENTS.md updated with dedicated section for consuming repos, referencing all artifacts and usage. README.md updated with prominent links and call-to-action. Added quickstart snippet in docs/ and adoption checklist. Examples reference scored catalog and pilot.
Acceptance met.
## Document production path and adapters
```task
id: FEATURE-WP-0004-T06
status: todo
status: done
priority: medium
state_hub_task_id: "a2058492-2630-4463-b20c-5a761043d08b"
```
@@ -158,15 +155,15 @@ From WP-0003 open questions and MVP scope:
- Note any canon extensions needed (e.g., more on adapters or governance).
- Cross-reference PRD non-goals and future work.
Acceptance:
- Clear, realistic guidance on moving from MVP adoption to production.
- Identifies gaps for follow-on work (e.g., new WP for adapters).
**Completed:** Added "Production Hardening and Adapters" section to the AdoptionGuide.md with guidance on real providers, config, etc. Noted gaps and cross-references to PRD. Added stub in ConsumerBrief. Identifies future WP needs.
Acceptance met.
## Create consumer workplan template and integration examples
```task
id: FEATURE-WP-0004-T07
status: todo
status: done
priority: low
state_hub_task_id: "e1412498-7853-4ee4-aa9d-4174ccf37497"
```
@@ -175,9 +172,9 @@ state_hub_task_id: "e1412498-7853-4ee4-aa9d-4174ccf37497"
- Add 1-2 example stubs (e.g., for a hypothetical "my-new-app" or reference to helix-forge patterns).
- Ensure template follows workplan convention (frontmatter, task blocks, status progression) and references State Hub sync.
Acceptance:
- Template is ready for copy-paste into new projects.
- Examples show how to tie adoption to the scored catalog and MVP SDK.
**Completed:** Created docs/templates/consumer-workplan-template.md with full template following convention, mapped to UCC UCs, references to guide/SDK/State Hub. Added example stub for "my-new-app-adopt-feature-control.md" in docs/templates/examples/.
Acceptance met.
## Non-functional, boundaries, and acceptance criteria (overall)