Skip to content
Back to skills

Infer Features From Code

ASecurity

Infer the 3-tier domain → capability → feature catalog (scope/features.yaml) from scan-index.json during /codify by enumerating per-capability modules, routes, and entry-point wiring, then assigning a 5-point status (planned | development | rollout | released | cleanup) from release tags, churn, test presence, and deprecation markers. Used exclusively by product-keeper after infer-enriched-capabilities-from-code.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
developmentgoexpressfastapirailsspringgitapifrontend

Works with

  • api

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add kapilvirenahuja/garura --skill infer-features-from-code --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Infer Features From Code?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Infer Features From Code
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kapilvirenahuja-infer-features-from-code/badge)](https://www.skillsdirectory.com/skills/kapilvirenahuja-infer-features-from-code)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: infer-features-from-code
description: Infer the 3-tier domain → capability → feature catalog (scope/features.yaml) from scan-index.json during /codify by enumerating per-capability modules, routes, and entry-point wiring, then assigning a 5-point status (planned | development | rollout | released | cleanup) from release tags, churn, test presence, and deprecation markers. Used exclusively by product-keeper after infer-enriched-capabilities-from-code.
version: 0.1.0
user-invocable: false
model: sonnet
allowed-tools: Read, Write, Grep, Glob
deprecated: true
deprecated_note: '#434 ProductOS realignment — superseded by the command model; retained for Phase E reference, not installed'
---

# infer-features-from-code

Called by `product-keeper` during `/codify`, after
`infer-enriched-capabilities-from-code`. Produces `scope/features.yaml` at
`{stm_base}/{issue}/evidence/codify/proposals/scope/features.yaml`.

## Purpose

Once domains and enriched capabilities are proposed, the spec trunk needs
the canonical 3-tier feature catalog — the same artifact `manage-features`
writes in `/specify` Stage 4b. In `/codify` the feature list and per-feature
status must be reverse-engineered from code. This skill reads
`scan-index.json`, walks every selected capability from the sibling
proposals, enumerates feature candidates from modules / routes / entry-point
wiring, assigns each feature a 5-point status via the heuristic below, and
records every status decision in a `status_inferences` manifest for
downstream surfacing.

Tier-2 inference. `learning_category: product`. `sub_category: null`.

## Input

Receive from `product-keeper` via JSON contract. All paths absolute.

| Field | Required | Description |
|-------|----------|-------------|
| `scan_index_path` | yes | `scan-index.json` produced by `scan.py`. |
| `stm_base` | yes | STM root (`stm.base-path` in `.garura/core/config.yaml`). |
| `issue` | yes | Issue number (STM namespace). |
| `output_path` | yes | `{stm_base}/{issue}/evidence/codify/proposals/scope/features.yaml`. |
| `decision_manifest_path` | yes | `{…}/scope/decision-manifest-infer-features.yaml`. |
| `status_inferences_path` | yes | `{…}/scope/status-inferences-infer-features.yaml`. |
| `resolution_trace_path` | yes | `{…}/scope/resolution-trace-infer-features.yaml`. |
| `related_proposal_paths` | yes | Block with `scope_path`, `enriched_capabilities_path`, `domain_selection_path`. |
| `ltm_context` | yes | `{product_base, core_base, query_domains, locked_artifacts}`. |

## Process

### 1. Validate inputs

- `scan_index_path` parses as JSON with keys `manifests`, `trees`,
  `entry_points`, `docs`, `config_files`, `frontend_detection`, `patterns`,
  `git`. Missing → `scan_index_missing`.
- Every file in `related_proposal_paths` exists and is valid YAML. Missing
  any → `missing_related_proposal`. Parent dirs for the four output paths
  exist or are creatable.

### 2. Resolution Protocol walk

Per `core/components/memory/standards/rules/resolution.md`, record every
probe in `resolution_trace_path`. R1 skipped (codify bootstrap; reason
`"codify-bootstrap"`). R2 probes `{product_base}/scope/features.yaml` — if
present, read **informationally only**; prior IDs, name shapes, and status
values land in the trace. The skill still emits a fresh proposal; it does
NOT short-circuit to `alignment_confirmed`. Conflicts between an existing
catalog and the inferred proposal are surfaced as `status_inferences`
entries at `tier=mid`. R3 / R4 skipped (feature taxonomy is project-specific).

### 3. Read related proposals

Pull selected domain + capability slugs from `scope.yaml` +
`domain-selection.yaml`. Pull per-capability notes (module hints, anchored
framework idioms) from `enriched-capabilities.yaml`. These feed Step 4.

### 4. Enumerate feature candidates per capability

For each selected capability, walk the scan index through these signal lanes:

1. **Top-level modules under `src/`.** Direct children of the source root
   from `trees`. When convention nests deeper (`src/{capability}/*`), descend
   one more level under the capability subfolder.
2. **Per-capability subfolders.** When the capability slug matches a
   directory name, scope enumeration to that subtree. Each direct subfolder
   is a feature candidate.
3. **Route / handler counts.** From `entry_points` and
   `patterns.framework_idioms`, count routes (Express / FastAPI / Gin /
   Spring / Rails) per module. Multiple distinct route groups → multiple
   features; a single route group → one feature.
4. **Entry-point wiring.** From `entry_points`, identify modules registered
   in application bootstrap (main, app, server, cmd). Unregistered folders
   land as scaffold-only candidates (feeds `planned` status in Step 5).
5. **`patterns.naming_suffix_counts`.** Suffix counts (`*Repository`,
   `*Service`, `*Controller`, `*Handler`) confirm the feature exists but do
   NOT on their own raise the status above `planned`.

Name each feature from the module name in title case, plus — when available —
the nearest matching README header (`docs.readme_preview`). Names are noun
phrases of 3–10 words per `manage-features` R2. ID regex `^[A-Z]{2,4}-F\d{3}$`;
PREFIX is the capability's canonical abbreviation from
`enriched-capabilities.yaml`. Number features within a capability from
`F001` in enumeration order.

### 5. Assign per-feature status — heuristic (first match wins)

1. **`cleanup`** — module present AND a deprecation marker is hit. Markers:
   literal `@deprecated` annotation in any module file, `DEPRECATED` in
   module README, or `deprecated: true` frontmatter. Evidence: the exact
   file path + ≤200-char excerpt.
2. **`released`** — module present AND a release tag in `git.tags_recent`
   references the module path OR a semver ≥ 1.0 tag exists for the repo
   AND test signal is present for the module
   (`patterns.test_framework_signals` intersects the module path) AND the
   module does NOT appear in `git.churn_top` (churn is below the hot-file
   threshold → "stable").
3. **`rollout`** — module present AND test signal present AND either no
   release tag yet OR all tags are pre-1.0 (built, not fully released).
4. **`development`** — module present AND either partial test coverage
   (tests exist for some files in the module but not all) OR the module
   appears in `git.churn_top` (active churn). Active work-in-flight signal.
5. **`planned`** — module referenced in README, config, or an entry-point
   import path BUT the directory is absent OR contains only scaffolding
   (empty `index.ts`, lone `types.ts`, naming-suffix stubs with zero
   route/handler wiring).
6. **Fallback → `planned`** — no rule resolves. Recorded at `tier=low`
   with `grounding_source=insufficient-signal`.

Confidence per status:
- `high` — ≥ 2 signal lanes agree (e.g., release tag + test + low churn).
- `medium` — 1 strong signal OR 2 weak signals.
- `low` — fallback or single weak signal.

### 6. Build the 3-tier tree

Nest features under their capability; nest capabilities under their domain.
Slugs come verbatim from `domain-selection.yaml.selected_domains[].id` and
`enriched-capabilities.yaml.capabilities[].id` — never relocate, never
invent.

Capability-level status rollup (per `manage-features` precedence): any
`released` → `released`; else any `rollout` → `rollout`; else any
`development` → `development`; else any `cleanup` → `cleanup`; else
`planned`. Domain-level status is freeform (pass through from enriched
record or leave blank) — C4 of `manage-features` forbids coercing it to
the 5-point enum.

Compute summary counters: `domains`, `capabilities`, `features_total`,
`features_planned`, `features_development`, `features_rollout`,
`features_released`, `features_cleanup`.

### 7. Write status-inferences manifest + primary artifact

Write order (manifests first so mid-write crashes leave an audit trail):
resolution trace → status inferences → decision manifest → primary artifact.
Every per-feature status lands in `status_inferences_path` with
`feature_id`, `assigned_status`, `heuristic_rule` (1–6 above), `signals`
(concrete evidence paths), `confidence`, `alternatives_considered`, `tier`.

## Output

### Primary artifact — `scope/features.yaml`

```yaml
meta:
  source_type: "inferred_from_code"
  evidence:
    - "scan-index.json#/trees"
    - "scan-index.json#/entry_points"
    - "scan-index.json#/patterns/framework_idioms"
    - "scan-index.json#/patterns/naming_suffix_counts"
    - "scan-index.json#/patterns/test_framework_signals"
    - "scan-index.json#/git/tags_recent"
    - "scan-index.json#/git/churn_top"
    - "scan-index.json#/docs/readme_preview"
  confidence: "high" | "medium" | "low"   # aggregate over features
  learning_category: "product"
  sub_category: null
  tier: 2
summary:
  domains: <int>
  capabilities: <int>
  features_total: <int>
  features_planned: <int>
  features_development: <int>
  features_rollout: <int>
  features_released: <int>
  features_cleanup: <int>
domains:
  - id: "<domain-slug>"
    status: "<freeform or blank>"
    capabilities:
      - id: "<capability-slug>"
        status: "planned | development | rollout | released | cleanup"
        features:
          - id: "<PREFIX-Fnnn>"
            name: "<noun phrase, 3–10 words>"
            status: "planned | development | rollout | released | cleanup"
            description: "<1–2 sentences from module README/docstring>"
            evidence_paths:
              - "<repo-relative module path>"
              - "scan-index.json#<pointer>"
            confidence: "high | medium | low"
```

### Decision manifest — `decision-manifest-infer-features.yaml`

Standard Garura shape (`schema_version`, `skill`, `generated_at`,
`decisions[]`). One entry per inferred status, per name massaged away from
the raw module identifier, and per capability-level rollup. Each carries
`decision_id`, `decision_type`, `tier`, `grounding_source`,
`recommendation`, `alternatives`, `chosen_reason`.

### Return contract

```yaml
status: success
artifact_path: "<output_path>"
decision_manifest_path: "<decision_manifest_path>"
status_inferences_path: "<status_inferences_path>"
resolution_trace_path: "<resolution_trace_path>"
features_count: <int>
capabilities_count: <int>
domains_count: <int>
overall_confidence: "high" | "medium" | "low"
```

## Failure Modes

- `missing_related_proposal` — scope.yaml / enriched-capabilities.yaml /
  domain-selection.yaml absent or unparseable.
- `scan_index_missing` — `scan_index_path` absent or not valid JSON.
- `ltm_resolution_failed` — R2 errored (existing features.yaml not valid
  YAML, or product_base resolves outside the repo sandbox).
- `insufficient_signal` — scan present but zero modules under any selected
  capability AND zero entry-point wiring AND zero README references. No
  candidate list is inferable; product-keeper falls back to human input.

## Boundaries

- Read-only against `scan_index_path`, `related_proposal_paths`, and (for
  R2) `{product_base}/scope/features.yaml`. Emits a proposal only — writing
  to `.garura/product/scope/features.yaml` is a downstream `/garura:enrich` action.
- Does NOT re-run `scan.py`, invent capabilities or domains outside the
  sibling proposals, or produce intent epics.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…