Tutorial: Build a simple Kanban on the ontology¶
Goal of this tutorial: learn openKMS ontology by building a small Kanban-style project board on it—object types, links, a few cards, and one decision Function. Kanban is a classic teaching vehicle in software (columns, cards, owners, blockers); here it is the outcome, not only a metaphor.
Optional next case study: Tushare market ontology DIY (markets)—same layering, different domain.
| Audience | Software builders new to Ontology Manager / Object Explorer / Function Editor |
| Time | ~60–90 minutes (concepts + lab) |
| You will leave with | A live mini-board: Project, WorkItem, Person, dependsOn, plus a published FoO such as priority or dependency analysis |
Product reference: Ontology · Ontology Functions · Goals
openKMS does not ship a Kanban board UI in Object Explorer. “Board” means: typed work items with a status/column property, browseable in Object Explorer, optionally graphed in Neo4j, with FoO for AI-assisted decisions and Actions to create / update / move / delete cards (
editsapply). Optional hosted UIs live under Apps (registered Kubernetes Services)—not an in-product A2UI board designer.
1. Why this goal? (ontology as knowledge + Kanban as teacher)¶
openKMS is a knowledge system. Knowledge is not only prose (docs, wiki, KB). A large part is structured: which projects exist, which cards sit in which column, what blocks what, who is overloaded, which priority rule is official. That is ontology knowledge—a first-class reason openKMS supports Ontology Manager, Object Explorer, and Functions (Goals).
| Kind of knowledge | Surface |
|---|---|
| Prose / evidence | Documents, articles, wiki, KB |
| Terms & maps | Glossaries, knowledge map |
| Typed work, relations, decision logic | Ontology |
A Kanban board is an ideal teaching target because every developer already knows:
Cards have owners, estimates, and blockers. If you can model that as ontology + FoO, you can model almost any operational domain later (including markets).
2. Learning goals¶
By the end you can:
- Explain why the board state is knowledge, not “just a project tool.”
- Name data source / dataset / object type / instance / link / index / Function.
- Create Project · WorkItem · Person (and links) for a toy board.
- Put a few cards in columns via a status property and find them in Object Explorer.
- Publish one FoO that helps an AI decide (priority, dependencies, or capacity).
- (Optional) Bind Actions that create / update / move / delete cards via
edits(create/modify/deleteapply). - (Optional) Know that Apps can host an external UI for the same ontology APIs (module registration)—board DIY UI is not built into openKMS.
3. Concepts you need for the board (short)¶
Data source (Postgres) → where tables live (optional for hand-entered instances)
Dataset → registered table (optional; use if you sync or seed SQL)
Object type → Project / WorkItem / Person
Instance → one project, one card, one person
Link type → belongsTo / assignedTo / dependsOn
Index (Neo4j) → Cypher (“what blocks WI-2?”)
Function (FoO) → suggestWorkItemPriority / workItemDependencyClosure
Action → create / update / move / delete (edits create|modify|delete; platform applies)
Apps (optional) → hosted external UI registered to a Service (see Step F)
Dataset vs object type: a dataset is a table registration; an object type says those rows (or hand-created instances) mean WorkItems. For this lab you may skip datasets and create instances directly in Object Explorer—fastest path for teaching. Add datasets when you sync from Jira/Linear or seed Postgres.
FoO: Function whose inputs include object ids (e.g. work_item_id). Not OOP WorkItem.method() (Palantir FoO). Mark those fields with x-ontology (kind: object_type, type_name: WorkItem) on input_schema / output_schema so Manager and the Overview Graph treat them as Object Type links—not bare strings.
Suite Apps: Manager (schema) · Explorer (cards / Cypher) · Function Editor (FoO).
4. Design the mini-board (before clicking)¶
4.1 Object types¶
| Object type | Key ideas | Example properties |
|---|---|---|
| Project | One board / delivery effort | name, key (e.g. DEMO) |
| WorkItem | One card | title, status (backlog | in_progress | review | done), estimate, priority |
| Person | Assignee | name, email or handle |
status is the Kanban column for this tutorial. Keep the enum small and documented.
4.2 Link types¶
| Link | From → To | Why |
|---|---|---|
| belongsTo | WorkItem → Project | Card on a board |
| assignedTo | WorkItem → Person | Owner |
| dependsOn | WorkItem → WorkItem | Blockers / dependency analysis |
4.3 Decision Functions (pick one for the lab)¶
| apiName | Purpose |
|---|---|
suggestWorkItemPriority |
Score a card (blocked? due soon? estimate?) |
workItemDependencyClosure |
List transitive dependsOn blockers |
teamCapacitySnapshot |
Open items per Person on a Project |
Agents and humans should call the published Function instead of ad-hoc SQL.
5. Lab — build it¶
Prerequisites¶
- openKMS running (Quickstart).
- For FoO execute: ontology-function-service up.
- Permissions to create object/link types, instances, and Functions.
- Optional Neo4j data source if you want Cypher on dependencies.
Step A — Create object types (Manager)¶
In Ontology Manager → Object types, create:
- Project — properties
name(string),key(string); mark master if you use document labels later. - Person —
name,handle. - WorkItem —
title,status,estimate(number), optionalpriority(number or string).
No dataset required for this toy board. (If you prefer tables: create pm.* in Postgres, register Datasets, then bind—same OT names.)
Step B — Create link types¶
- belongsTo — WorkItem → Project (many-to-one).
- assignedTo — WorkItem → Person.
- dependsOn — WorkItem → WorkItem.
Step C — Seed a board (Object Explorer)¶
Create instances (names illustrative):
| Type | Instance |
|---|---|
| Project | Demo Board (key=DEMO) |
| Person | Ada, Lin |
| WorkItem | WI-1 Setup board (status=done) |
| WorkItem | WI-2 Write FoO (status=in_progress) |
| WorkItem | WI-3 Dependency demo (status=backlog) |
Links:
- Each WorkItem belongsTo Demo Board.
WI-2assignedTo Ada;WI-3assignedTo Lin.WI-3dependsOnWI-2(blocked until FoO exists).
“See the board”: Object Explorer → WorkItem list → sort/filter by status. That is your Kanban view for this tutorial. Optional: Index types to Neo4j and Cypher for dependency paths.
Step D — Publish one FoO (Function Editor → Manager Publish)¶
Declare object ids as WorkItem refs on the Function schema (callers may pass either key):
{
"type": "object",
"anyOf": [{"required": ["work_item_id"]}, {"required": ["object_id"]}],
"properties": {
"work_item_id": {
"type": "string",
"x-ontology": {"kind": "object_type", "type_name": "WorkItem"}
},
"object_id": {
"type": "string",
"x-ontology": {"kind": "object_type", "type_name": "WorkItem"}
}
}
}
Pass that JSON as --input-schema-json on create/save-version (Manager Overview can set the same fields structurally). Without x-ontology, the Graph and schema_relations treat ids as primitives even if the Function affiliates to an Object Type.
Example contract for priority (implement scoring however you like; keep input/output stable):
from openkms_functions import Client, function
@function
def execute(input: dict, client: Client) -> dict:
"""FoO: suggest priority for one WorkItem (Kanban decision helper)."""
wid = (input.get("work_item_id") or input.get("object_id") or "").strip()
if not wid:
return {"error": "work_item_id (or object_id) is required"}
item = client("WorkItem").fetch_one(wid)
# Soft heuristic for teaching—replace with real rules later.
status = str((item or {}).get("status") or (item or {}).get("properties", {}).get("status") or "")
score = 50
reasons = []
if status == "blocked" or status == "backlog":
score += 10
reasons.append("waiting_or_backlog")
if status == "in_progress":
score += 20
reasons.append("already_in_progress")
return {
"work_item_id": wid,
"score": score,
"reasons": reasons,
"priority": "high" if score >= 70 else "medium" if score >= 55 else "low",
"hint": "Extend with dependsOn / assignee load when Client link APIs fit your data.",
}
Publish as suggestWorkItemPriority. Execute with the id of WI-3. Use client("WorkItem") / client("suggestWorkItemPriority") string api names via openkms_functions.
For dependency closure, BFS with server-filtered links. Input schema: required work_item_id with the same WorkItem x-ontology. Optional output schema marks blockers as an array of WorkItem ids:
{
"type": "object",
"properties": {
"work_item_id": {
"type": "string",
"x-ontology": {"kind": "object_type", "type_name": "WorkItem"}
},
"blockers": {
"type": "array",
"x-ontology": {"kind": "object_type", "type_name": "WorkItem"},
"items": {"type": "string"}
}
}
}
from openkms_functions import Client, function
@function
def execute(input: dict, client: Client) -> dict:
wid = (input.get("work_item_id") or "").strip()
seen: set[str] = set()
frontier = [wid]
blockers: list[str] = []
while frontier:
cur = frontier.pop()
if cur in seen:
continue
seen.add(cur)
for link in client.get_links("dependsOn", source_id=cur, limit=100):
tgt = link.get("target_object_id") or link.get("target_key_value")
if tgt and tgt not in seen:
blockers.append(str(tgt))
frontier.append(str(tgt))
return {"work_item_id": wid, "blockers": blockers}
Capacity-style FoO can use property filters: client("WorkItem").search(filters={"status": "in_progress"}, limit=200).
Step E — Actions: create / update / move / delete WorkItems¶
In Ontology Manager → Action types, create WorkItem actions with built-in rules — no Function required for simple CRUD:
| Intent | Rule type | Example api_name |
|---|---|---|
| Create | object_create |
createWorkItem |
| Edit | object_modify |
updateWorkItem |
| Delete | object_delete |
deleteWorkItem |
On the Rules tab you can limit writable fields (e.g. only status for a column-move action). Optional defaults in parameters (e.g. {"status": "done"}) pre-fill values on modify — useful for a Move to Done action that only needs object_id.
Action execute applies create / modify / delete on Explorer-style object instances (applied.created_ids / modified_ids / deleted_ids).
Custom logic (cross-type checks, computed fields, multi-step edits) still uses a published Function bound to a function rule type.
When creating a WorkItem object type, the Manager wizard can tick Create / Edit / Delete to add matching built-in action types automatically.
Try create from an App or API; try update / move / delete on a card in Object Explorer.
Step F — Optional hosted Apps (external UI)¶
Object Explorer (and Action / FoO APIs) are enough for this lab. If you later want a custom board UI, build it as a normal web app and register it under Apps (hosted Service + identity headers). openKMS no longer ships an in-product A2UI App designer for composing boards.
Story to remember: board state and rules live in the ontology; any visual board is your own UI calling the same Actions / Functions.
6. What success looks like¶
| Checkpoint | Pass criteria |
|---|---|
| Schema | Project, WorkItem, Person + three link types exist |
| Board | ≥3 WorkItems with different status values; visible in Explorer |
| Relations | At least one dependsOn and one assignedTo |
| Decision | Published FoO returns JSON for a card id |
| Write | Actions create / update / move / delete persist via edits (applied.created_ids / modified_ids / deleted_ids) |
| Story | You can explain: board state = ontology knowledge; FoO = shared decision rule; Explorer / Actions are enough without a custom board UI |
7. Anti-patterns¶
| Avoid | Prefer |
|---|---|
| Building a custom Kanban SPA before OT/links exist | Model the board in ontology first |
| Expecting a published FoO alone to persist card edits | FoO computes; Actions apply edits |
| Indexing every status-change event | Index WorkItem/Person/Project + dependsOn |
| Calling Jira on every Function execute | Sync or hand-enter instances; FoO reads ontology |
| Secret priority formulas in chat only | Published FoO with version + audit |
| Treating the board as “not knowledge” | Columns, blockers, and priority rules are knowledge |
8. Glossary (board-oriented)¶
| Term | On the Kanban lab |
|---|---|
| Object type | Project / WorkItem / Person |
| Instance | One card or person |
| status property | Kanban column |
| dependsOn | Blocker edge for analysis |
| FoO | Decision helper on a card / project |
| Action | Intentional write (create / modify / delete apply) |
| Apps | Optional hosted external UI registered to a Service |
| Index | Optional graph for dependency Cypher |
Full stack glossary remains in older revisions’ spirit: data source, dataset, publish, Action, TSP—see Ontology.
9. Next steps¶
| Path | When |
|---|---|
Enrich FoO (workItemDependencyClosure, teamCapacitySnapshot) |
You want stronger AI decisions on the same board |
| Apps (hosted modules) | Register your own board UI as a hosted App |
| Bind datasets / connector sync from a real tracker | You outgrow hand-entered cards |
| Tushare market ontology DIY | Practice the same pattern on market data |
| Ontology Functions | Deeper authoring / SDK |
10. Check yourself¶
- Why is “cards on a board” ontology knowledge in openKMS, not only a UI widget?
- Which property plays the role of Kanban columns in this lab?
- Name one FoO that helps with dependencies, priority, or resources.
- If FoO suggests a new priority, what still has to run for the card to persist that change?
If you can answer and your Demo Board instances exist, you finished this tutorial’s goal.