Folders in a System
Arrange the Resources tracked by a System into a tree from Python (istari-digital-client 13.2.1). A folder is a path prefix on a tracked Resource in a commit, not an object you create: the web app shows the names you typed, and the API stores an encoded path.
On 13.0.x, the branch method used below is named branch.files() rather than branch.resources(); everything else on this page is the same.
For what folders are and when a Subsystem fits better, see Systems and Subsystems. For the same task in the browser, see Folders.
Prerequisites
- A configured
Client— see SDK setup. - Editor (or above) on the target System.
Path encoding
The write form is the System id without hyphens, then one dot-separated segment per folder level. Letters and digits stay literal; every other ASCII character becomes _XX — two uppercase hex digits of its code:
| You mean | Stored as |
|---|---|
10-Analysis | 10_2DAnalysis |
tier 1 native | tier_201_20native |
v1_extracted | v1_5Fextracted |
Folder names must be ASCII. A bare -, space, or _ in a segment is rejected.
import re
def folder_path(system_id: str, *names: str) -> str:
"""The `<32-hex system id>.<label>…` form that NewTrackedFile(folder_path=…) expects."""
def label(name: str) -> str:
if not name.isascii():
raise ValueError(f"folder names must be ASCII: {name!r}")
return re.sub(r"[^A-Za-z0-9]", lambda m: f"_{ord(m.group()):02X}", name)
return ".".join([system_id.replace("-", ""), *(label(n) for n in names)])
Reading a path back
branch.resources() returns the branch's tracked Resources, each carrying a path. That path ends with the Resource's own id as a final segment (<system-hex>.<folders…>.<file-hex>); the path you write must not include it, so strip the trailing 32-hex segment before reusing a stored path:
def folders_of(path: str | None) -> list[str]:
"""The encoded folder segments of a tracked Resource path, without the file-id tail."""
hex32 = re.compile(r"^[0-9a-f]{32}$")
return [] if not path else [s for s in path.split(".")[1:] if not hex32.match(s)]
def decode_label(segment: str) -> str:
"""`10_2DAnalysis` -> `10-Analysis`, the name the web app shows."""
return re.sub(r"_([0-9A-F]{2})", lambda m: chr(int(m.group(1), 16)), segment)
Committing into a folder
branch.commit() on the Istari facade takes only add and remove: it cannot set a folder, and it rebuilds every carried tracked file without one, so existing folders are dropped. Commit folder layout with Client instead — build a configuration in which every tracked Resource carries its folder_path, then move the branch tag to the snapshot that configuration produced.
Both clients take the same Configuration, so one credential serves the branch read and the commit:
from istari_digital_client import (Client, NewSystemConfiguration, NewTrackedFile,
TrackedFileSpecifierType, UpdateTag)
from istari_digital_client.sdk import Istari
istari, client = Istari(configuration), Client(configuration)
system = istari.systems.get(system_id)
baseline = system.get_branch("baseline")
# Carry what is already on the branch, each file keeping its own folder.
keep = [
NewTrackedFile(
specifier_type=TrackedFileSpecifierType(tracked.specifier_type),
file_id=tracked.file_id,
pinned_file_revision_id=tracked.pinned_file_revision_id,
**({"folder_path": folder_path(system.id, *(decode_label(s) for s in folders_of(tracked.path)))}
if folders_of(tracked.path) else {}),
)
for tracked in baseline.resources()
]
# Add the new Resource into 10-Model/. Omit folder_path to put it at the root.
new = [NewTrackedFile(specifier_type=TrackedFileSpecifierType.LATEST,
file_id=resource.file_id,
folder_path=folder_path(system.id, "10-Model"))]
configuration_ = client.create_configuration(
system.id, NewSystemConfiguration(name="add bracket", tracked_files=keep + new))
snapshot = next(s for s in client.list_snapshots(system.id, page=1, size=100).items
if s.configuration_id == configuration_.id)
tag = next(t for t in client.list_tags(system.id).items if t.tag == "baseline")
client.update_tag(tag.id, UpdateTag(snapshot_id=snapshot.id))
Every Resource you leave out of tracked_files leaves the branch, and every one you carry without a folder_path moves to the root — so build keep from the branch you are about to replace rather than from memory.
A runnable version of all of this is istari_quickstart_13_2_1.py, and the istari-folders skill is the same recipe for an AI coding agent.
Related reference
- Branching and change requests — commits, branches, and change requests on a System.
- Systems API —
create_configuration,list_snapshots,update_tag, and the folder list, rename, and move calls onClient. - Linking to the web app — the URL that opens a Resource in its folder.