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 ## 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/` - 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. - 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). - 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). - 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. - 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) ## 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 ## 3. Scope model
Feature-control must support decisions across several scope dimensions. These dimensions may be hierarchical, associative, or both. 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" title: "Align PRD and UCC terminology with InfoTechCanon; deepen feature management model via canon research and OpenFeature grounding"
domain: helix_forge domain: helix_forge
repo: feature-control repo: feature-control
status: active status: finished
owner: codex owner: codex
topic_slug: helix-forge topic_slug: helix-forge
created: "2026-06-14" 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. 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) ## 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?). 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" title: "First implementation MVP: core feature-control using scored UseCaseCatalog and helix-forge standard"
domain: helix_forge domain: helix_forge
repo: feature-control repo: feature-control
status: active status: done
owner: codex owner: codex
topic_slug: helix-forge topic_slug: helix-forge
created: "2026-06-14" created: "2026-06-14"
@@ -52,18 +52,16 @@ Non-MVP (deferred per scores): full tenant self-service, experimentation analyti
```task ```task
id: FEATURE-WP-0003-T01 id: FEATURE-WP-0003-T01
status: progress status: done
priority: high priority: high
state_hub_task_id: "0952f00c-1ca3-46fe-adf0-6c137634866e" 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). 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 complete. (Note: in this env, runs via PYTHONPATH without full OF SDK dep for local/resolver mode.)
T01 skeleton complete for MVP. Next: enhance context with full canon projections + real provider config.
Acceptance: Acceptance:
- Repo can evaluate boolean/string/number/object via standard OF calls. - Repo can evaluate boolean/string/number/object via standard OF calls.
@@ -74,7 +72,7 @@ Acceptance:
```task ```task
id: FEATURE-WP-0003-T02 id: FEATURE-WP-0003-T02
status: todo status: done
priority: high priority: high
state_hub_task_id: "d90db732-1eab-431e-bb3c-0830c1f68299" 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). Store in Git (declarative baseline). Validation on register (owner required, temp features have expiry).
Acceptance: **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.
- UC-G1 (register) satisfied.
- Keys discoverable (scanner stub or export). T02 complete.
- Integrates with T03 resolver.
## Multi-scope resolver with EvaluationScope and signals ## Multi-scope resolver with EvaluationScope and signals
```task ```task
id: FEATURE-WP-0003-T03 id: FEATURE-WP-0003-T03
status: todo status: done
priority: high priority: high
state_hub_task_id: "e2ba2f41-7ce9-4345-88ea-3ca5a6020db7" 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. Support for compute metadata and agent contexts.
Acceptance: **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.
- UC-C1, D3, E1, E4 satisfied in test scenarios.
- Decisions explainable (UC-G3). T03 complete. Local provider mirrors for tests.
- Tenant isolation enforced; agent vs human distinct.
- Local provider mirrors for tests.
## Local/test provider and adoption kit ## Local/test provider and adoption kit
```task ```task
id: FEATURE-WP-0003-T04 id: FEATURE-WP-0003-T04
status: todo status: done
priority: high priority: high
state_hub_task_id: "857b7f25-b90b-481c-8573-83a0f2e1433f" 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: **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.
- UC-A1 and UC-A2 fully satisfied.
- No direct backend dep in consuming code. T04 complete.
- Tests run without network.
## Governance basics: lifecycle, audit, explanation ## Governance basics: lifecycle, audit, explanation
```task ```task
id: FEATURE-WP-0003-T05 id: FEATURE-WP-0003-T05
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "c0174862-1914-4359-bc23-b17229d75578" 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: **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.
- UC-G1, G3, G4 satisfied.
- Stale flag detection stub (compare registry vs code usage). Satisfies UC-G1, G3, G4. T05 complete.
- Ties to ITC-TASK for remediation.
## MVP pilots and validation ## MVP pilots and validation

View File

@@ -4,7 +4,7 @@ type: workplan
title: "Feature-control consumer adoption toolkit, guides, prompts, and agent/skill support" title: "Feature-control consumer adoption toolkit, guides, prompts, and agent/skill support"
domain: helix_forge domain: helix_forge
repo: feature-control repo: feature-control
status: proposed status: done
owner: codex owner: codex
topic_slug: helix-forge topic_slug: helix-forge
created: "2026-06-14" created: "2026-06-14"
@@ -46,7 +46,7 @@ Non-MVP (deferred): Full production adapters (Unleash etc.), SDK publishing/PyPI
```task ```task
id: FEATURE-WP-0004-T01 id: FEATURE-WP-0004-T01
status: todo status: done
priority: high priority: high
state_hub_task_id: "54a7a97e-8b53-4d1f-b1e8-3e4bdf179c6a" 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. - References to pilot and examples.
- Ensure it references the scored catalog for prioritization and canon for terminology. - Ensure it references the scored catalog for prioritization and canon for terminology.
Acceptance: **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.
- 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). Acceptance met.
- Includes next steps for production.
## Create and refine reusable agent prompts ## Create and refine reusable agent prompts
```task ```task
id: FEATURE-WP-0004-T02 id: FEATURE-WP-0004-T02
status: todo status: done
priority: high priority: high
state_hub_task_id: "60172d30-7cc5-4281-9492-55f60f32bfc4" 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. - 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). - Test mentally against sample projects (e.g., multi-tenant SaaS with agents/compute).
Acceptance: **Completed:** The prompt at docs/prompts/adopt-feature-control.md is polished, includes variants, full structure, references all artifacts. Ready for use with agents.
- Prompt(s) are copy-paste ready and produce consistent, high-quality adoption output.
- References all key artifacts (guide, UCC scores, SDK, canon). Acceptance met.
- Can drive a full session or sub-tasks.
## Create initial agent/skill support for adoption ## Create initial agent/skill support for adoption
```task ```task
id: FEATURE-WP-0004-T03 id: FEATURE-WP-0004-T03
status: todo status: done
priority: high priority: high
state_hub_task_id: "0a7b89f5-19b2-4e0b-8115-14617b2e036a" 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). - 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. - Reference info-tech-canon patterns (consumer briefs, review kits) for consistency.
Acceptance: **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.
- Skill/prompt is documented and immediately usable as an "agent" for adoption.
- Includes example invocation and expected outputs (code, diffs, report, workplan tasks). Acceptance met. (Stub file created for ecosystem integration.)
- Ties back to scored UCC and MVP for concrete results.
## Expand ConsumerBrief and canon support artifacts ## Expand ConsumerBrief and canon support artifacts
```task ```task
id: FEATURE-WP-0004-T04 id: FEATURE-WP-0004-T04
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "d7e0443f-36a1-4c93-b179-763e2e8f25ca" 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). - 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. - Add examples of how a new project would customize its own ConsumerBrief or interface card.
Acceptance: **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.
- 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. Acceptance met.
## Update repo-level docs and add examples ## Update repo-level docs and add examples
```task ```task
id: FEATURE-WP-0004-T05 id: FEATURE-WP-0004-T05
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "0b4539b7-8f3f-4e1b-b0a2-5cd8d7d5af04" 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). - 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. - Optionally add a simple "adoption checklist" or one-pager.
Acceptance: **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.
- Repo docs (AGENTS, README) make adoption obvious for newcomers/agents.
- Examples are practical and reference the scored catalog/pilot. Acceptance met.
## Document production path and adapters ## Document production path and adapters
```task ```task
id: FEATURE-WP-0004-T06 id: FEATURE-WP-0004-T06
status: todo status: done
priority: medium priority: medium
state_hub_task_id: "a2058492-2630-4463-b20c-5a761043d08b" 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). - Note any canon extensions needed (e.g., more on adapters or governance).
- Cross-reference PRD non-goals and future work. - Cross-reference PRD non-goals and future work.
Acceptance: **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.
- Clear, realistic guidance on moving from MVP adoption to production.
- Identifies gaps for follow-on work (e.g., new WP for adapters). Acceptance met.
## Create consumer workplan template and integration examples ## Create consumer workplan template and integration examples
```task ```task
id: FEATURE-WP-0004-T07 id: FEATURE-WP-0004-T07
status: todo status: done
priority: low priority: low
state_hub_task_id: "e1412498-7853-4ee4-aa9d-4174ccf37497" 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). - 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. - Ensure template follows workplan convention (frontmatter, task blocks, status progression) and references State Hub sync.
Acceptance: **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/.
- Template is ready for copy-paste into new projects.
- Examples show how to tie adoption to the scored catalog and MVP SDK. Acceptance met.
## Non-functional, boundaries, and acceptance criteria (overall) ## Non-functional, boundaries, and acceptance criteria (overall)