# Understanding the Candidate Lifecycle for Person Profiles in Distilly

> Explore the Distilly candidate lifecycle for person profiles. Understand how created_at, updated_at, version, and status fields track and manage your persona artifacts automatically.

- Repository: [Tianyi Zhou/distilly](https://github.com/titanwings/distilly)
- Tags: deep-dive
- Published: 2026-09-10

---

**The candidate lifecycle for person profiles in Distilly tracks four core metadata fields—`created_at`, `updated_at`, `version`, and `status`—which are automatically managed across [`skill_writer.py`](https://github.com/titanwings/distilly/blob/main/skill_writer.py), [`skill_schema.py`](https://github.com/titanwings/distilly/blob/main/skill_schema.py), and [`version_manager.py`](https://github.com/titanwings/distilly/blob/main/version_manager.py) to ensure every persona artifact remains versioned and auditable.**

The `titanwings/distilly` repository treats every person profile as a versioned JSON/YAML artifact. Understanding the **candidate lifecycle for person profiles** is essential for managing how these persona metadata objects evolve from initial creation through archival.

## Core Lifecycle Attributes

Distilly persists four fields inside the `lifecycle` object of every profile's metadata:

- **`created_at`**: ISO-8601 timestamp set automatically on creation via `now_iso()`.
- **`updated_at`**: ISO-8601 timestamp refreshed each time the profile is modified.
- **`version`**: Human-readable version string (e.g., `v1`, `v2`) incremented during updates.
- **`status`**: Logical state of the profile, defaulting to `active`.

## Implementation Across the Codebase

### Initial Creation in [`skill_writer.py`](https://github.com/titanwings/distilly/blob/main/skill_writer.py)

The lifecycle begins when [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) generates a new persona. The module injects initial timestamps and version information into the metadata:

```python

# tools/skill_writer.py – Initial lifecycle injection

normalized_meta["lifecycle"]["created_at"] = normalized_meta.get("created_at", now_iso())
normalized_meta["lifecycle"]["updated_at"] = normalized_meta["lifecycle"]["created_at"]
normalized_meta["lifecycle"]["version"] = "v1"

```

### Schema Normalization in [`skill_schema.py`](https://github.com/titanwings/distilly/blob/main/skill_schema.py)

The schema layer ensures backward compatibility by backfilling missing lifecycle values. The [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py) module harmonizes metadata across the artifact tree:

```python

# tools/skill_schema.py – Lifecycle consistency enforcement

lifecycle = meta.setdefault("lifecycle", {})
meta["created_at"] = lifecycle.get("created_at", meta.get("created_at", now_iso()))
meta["updated_at"] = lifecycle.get("updated_at", meta.get("updated_at", meta["created_at"]))
meta["version"] = lifecycle.get("version", meta.get("version", "v1"))
lifecycle.setdefault("status", "active")

```

### Version Management in [`version_manager.py`](https://github.com/titanwings/distilly/blob/main/version_manager.py)

When existing profiles are updated or restored, [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) handles version bumping and timestamp refreshing:

```python

# tools/version_manager.py – Version bumping on update

meta["lifecycle"]["version"] = f"{target_version}_restored"
meta["lifecycle"]["updated_at"] = now_iso()

```

## Practical Examples

### Creating a New Persona

When you create a profile via the CLI, the lifecycle fields initialize automatically:

```bash
distilly new-persona --name "Alice" --output ./skills/alice

```

This command invokes [`skill_writer.py`](https://github.com/titanwings/distilly/blob/main/skill_writer.py), which sets `lifecycle.created_at` to the current ISO timestamp and initializes `version` as `"v1"`.

### Updating an Existing Profile

Modifications trigger the version manager to increment the version and update the timestamp:

```bash
distilly update-persona ./skills/alice --patch changes.md

```

Internally, this calls [`version_manager.py`](https://github.com/titanwings/distilly/blob/main/version_manager.py) to bump `lifecycle.version` (for example, to `"v2"`) and refresh `lifecycle.updated_at`.

### Inspecting Lifecycle Metadata Programmatically

You can read the lifecycle block directly from the meta file:

```python
import json
from pathlib import Path

meta = json.loads(Path("./skills/alice/meta.json").read_text())
print(meta["lifecycle"])

```

The output shows the complete audit trail:

```json
{
  "created_at": "2026-09-10T12:34:56Z",
  "updated_at": "2026-09-12T08:21:13Z",
  "version": "v2",
  "status": "active"
}

```

## Summary

- The **candidate lifecycle** manages four critical fields—`created_at`, `updated_at`, `version`, and `status`—stored within the `lifecycle` object of every persona profile.
- **[`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py)** initializes timestamps and version strings during the creation phase.
- **[`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py)** normalizes metadata, backfills missing values, and defaults `status` to `"active"`.
- **[`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py)** increments version strings and refreshes `updated_at` timestamps during profile modifications.
- Profiles transition through logical states via the `status` field, enabling archival workflows without physical deletion.

## Frequently Asked Questions

### How does Distilly handle missing lifecycle fields in legacy profiles?

When loading older artifacts, [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py) automatically backfills missing `created_at`, `updated_at`, and `version` values. The schema defaults `version` to `"v1"` and timestamps to the current time if no historical data exists, ensuring every profile conforms to the current lifecycle specification.

### Can the version string format be customized?

The codebase uses semantic-style strings such as `"v1"` or `"v2_restored"` (the latter appended by [`version_manager.py`](https://github.com/titanwings/distilly/blob/main/version_manager.py) during restore operations). While the system automatically manages these formats, the underlying implementation in [`version_manager.py`](https://github.com/titanwings/distilly/blob/main/version_manager.py) uses standard string formatting, allowing teams to modify conventions if they maintain the string type in the lifecycle object.

### What values are valid for the status field?

According to the implementation in [`tools/skill_schema.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_schema.py), the `status` field defaults to `"active"` when initialized via `lifecycle.setdefault("status", "active")`. The data structure accepts any string value, enabling custom states like `"archived"` or `"deprecated"` to suit specific workflow requirements.

### Where is the lifecycle metadata physically stored?

The lifecycle object resides inside the [`meta.json`](https://github.com/titanwings/distilly/blob/main/meta.json) or [`meta.yaml`](https://github.com/titanwings/distilly/blob/main/meta.yaml) file within each persona directory. [`tools/skill_writer.py`](https://github.com/titanwings/distilly/blob/main/tools/skill_writer.py) creates this file initially, while subsequent updates from [`tools/version_manager.py`](https://github.com/titanwings/distilly/blob/main/tools/version_manager.py) modify the lifecycle values in place.