feat(agent-id): canonical o2r agent-id generator (lowercase 2-letter+2-digit, confusable chars excluded) + org-shortname map + shared test vector #302

Closed
opened 2026-06-26 17:44:36 +00:00 by coilysiren · 2 comments
Owner

Goal

Make aos the canonical home for two cross-repo agent-naming primitives that ward and cli-guard consume:

  1. the o2r ID generator - a short agent id, lowercase, 2 letters + 2 digits (e.g. ab81), excluding confusable / similar-sounding characters.
  2. the org-shortname map - long Forgejo org -> short token for container names (so dozzle is not blown out).

o2r ID generator

  • Form: exactly 2 lowercase ASCII letters then 2 digits - ab81, cd92. [decided: lowercase]
  • Excluded characters: recover the exact exclusion set + rules from the o2r source - the archived coilyco-flight-deck/otel-a2a-relay (and its o2r CLI). The rules drop characters that look alike (i/l/1, o/0) and sound alike when spoken. Do not invent a new alphabet - lift the canonical one. If the source is genuinely unavailable, propose the excluded set and flag it for Kai to ratify against the original rather than guessing silently.
  • Implement the canonical generator in Python in aos (e.g. agentic_os/agent_id.py, importable + a ward agent-id verb if useful). secrets-backed, uniform over the allowed alphabet.
  • Emit a committed shared test vector (agentic_os/agent_id_vectors.json): the allowed letter+digit alphabets and a set of seed->id samples from a seedable variant, so the Go port can assert byte-for-byte parity. This file IS the cross-language contract.

Org-shortname map

  • Canonical committed data in a neutral format (JSON) so Go can read/port it: coilyco-flight-deck -> flight, coilyco-gaming -> gaming, coilyco-bridge -> bridge. Unmapped org -> documented fallback (raw org, or the last - segment). agentic_os/org_shortnames.json + a small accessor.

Validation (freshness)

The vector and map are derived/data contracts. Add a test that the Python generator matches the committed vector (drift fails CI), and a decay-class=derived freshness marker on any rendered doc, so the Go port (cli-guard) can be checked against the same committed file.

Acceptance

  • agent_id generates lowercase 2-letter+2-digit ids over the canonical excluded-char alphabet, with tests + the committed vector.
  • org-shortname map committed with the three mappings + fallback, accessor + test.
  • Both are importable/readable by an external repo - the contract cli-guard's Go port (#177) and ward's naming rewrite (#387) build against.

Files

new agentic_os/agent_id.py, agentic_os/agent_id_vectors.json, agentic_os/org_shortnames.json (+ accessor), tests/, [tool.agentic-os.*]/ward.yaml verb if exposed. Exclusion rules sourced from coilyco-flight-deck/otel-a2a-relay. Do this first - feeds cli-guard #177 and ward #387.

When done

Run the gate, commit, push to main, update docs/FEATURES.md, comment the vector path on #177 and #387.

## Goal Make aos the **canonical home** for two cross-repo agent-naming primitives that ward and cli-guard consume: 1. the **o2r ID generator** - a short agent id, **lowercase, 2 letters + 2 digits** (e.g. `ab81`), excluding confusable / similar-sounding characters. 2. the **org-shortname map** - long Forgejo org -> short token for container names (so dozzle is not blown out). ## o2r ID generator - Form: exactly **2 lowercase ASCII letters then 2 digits** - `ab81`, `cd92`. [decided: lowercase] - **Excluded characters: recover the exact exclusion set + rules from the o2r source** - the archived `coilyco-flight-deck/otel-a2a-relay` (and its `o2r` CLI). The rules drop characters that **look** alike (i/l/1, o/0) and **sound** alike when spoken. **Do not invent a new alphabet - lift the canonical one.** If the source is genuinely unavailable, propose the excluded set and **flag it for Kai to ratify against the original** rather than guessing silently. - Implement the canonical generator in Python in aos (e.g. `agentic_os/agent_id.py`, importable + a `ward agent-id` verb if useful). `secrets`-backed, uniform over the allowed alphabet. - Emit a **committed shared test vector** (`agentic_os/agent_id_vectors.json`): the allowed letter+digit alphabets and a set of **seed->id** samples from a seedable variant, so the Go port can assert **byte-for-byte parity**. This file IS the cross-language contract. ## Org-shortname map - Canonical committed data in a **neutral format** (JSON) so Go can read/port it: `coilyco-flight-deck -> flight`, `coilyco-gaming -> gaming`, `coilyco-bridge -> bridge`. Unmapped org -> documented fallback (raw org, or the last `-` segment). `agentic_os/org_shortnames.json` + a small accessor. ## Validation (freshness) The vector and map are `derived`/data contracts. Add a test that the Python generator matches the committed vector (drift fails CI), and a `decay-class=derived` freshness marker on any rendered doc, so the Go port (cli-guard) can be checked against the same committed file. ## Acceptance - `agent_id` generates lowercase 2-letter+2-digit ids over the canonical excluded-char alphabet, with tests + the committed vector. - org-shortname map committed with the three mappings + fallback, accessor + test. - Both are importable/readable by an external repo - the contract cli-guard's Go port (#177) and ward's naming rewrite (#387) build against. ## Files new `agentic_os/agent_id.py`, `agentic_os/agent_id_vectors.json`, `agentic_os/org_shortnames.json` (+ accessor), `tests/`, `[tool.agentic-os.*]`/ward.yaml verb if exposed. Exclusion rules sourced from `coilyco-flight-deck/otel-a2a-relay`. **Do this first** - feeds cli-guard #177 and ward #387. ## When done Run the gate, commit, push to `main`, update `docs/FEATURES.md`, comment the vector path on #177 and #387.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-agentic-os-302 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-04T09:00:19Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-agentic-os-302` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-04T09:00:19Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - canonical agent_id.py (lowercase 2-letter+2-digit), agent_id_vectors.json contract, org_shortnames.json + accessor, drift test, ward agent-id verb, freshness marker; landed on main, #177/#387 cross-referenced.

The nice surprise was that the o2r source was not genuinely unavailable - the archived otel-a2a-relay still clones from Forgejo, so I lifted the alphabet straight from channels/ids.py (ABCDEFGHJKMPQRSTUVWXYZ456789) instead of proposing a set for ratification. Even better, its comment pointed back at docs/dictatable-id-alphabet.md, which already lived in this repo - so the ground truth was never in question. No guessing needed.

The one thing that fought back was the issue examples themselves: ab81 / cd92 use 1 and 2, both excluded digits. My first test asserted is_valid("ab81") and it (correctly) failed. Good reminder that the illustrative examples show shape, not a valid id - I left a comment in the test saying so.

Confidence is high. The drift test regenerates the vector from the module and byte-compares, so the cross-language contract cannot silently rot, and the seeded algorithm (sha256, mod-indexed) is dead simple to port to Go. The lowercase-vs-uppercase divergence from o2r is the only judgement call, and it was pre-decided in the issue.

Rough edges / follow-ups worth noting, not filing yet: (1) I picked last---segment as the unmapped-org fallback (issue offered "raw org, or last segment") - if dozzle readability wants the raw org instead, that is a one-line flip. (2) Two concurrent containers pushed to main mid-run; my merges were clean auto-merges with no content conflicts, but it is a reminder that the naming-rewrite consumers (#387) should sync the JSON by a pinned ref, not a floating read.

WARD-OUTCOME: done - canonical agent_id.py (lowercase 2-letter+2-digit), agent_id_vectors.json contract, org_shortnames.json + accessor, drift test, ward agent-id verb, freshness marker; landed on main, #177/#387 cross-referenced. The nice surprise was that the o2r source was **not** genuinely unavailable - the archived `otel-a2a-relay` still clones from Forgejo, so I lifted the alphabet straight from `channels/ids.py` (`ABCDEFGHJKMPQRSTUVWXYZ456789`) instead of proposing a set for ratification. Even better, its comment pointed back at `docs/dictatable-id-alphabet.md`, which already lived in this repo - so the ground truth was never in question. No guessing needed. The one thing that fought back was the issue examples themselves: `ab81` / `cd92` use `1` and `2`, both **excluded** digits. My first test asserted `is_valid("ab81")` and it (correctly) failed. Good reminder that the illustrative examples show shape, not a valid id - I left a comment in the test saying so. Confidence is high. The drift test regenerates the vector from the module and byte-compares, so the cross-language contract cannot silently rot, and the seeded algorithm (sha256, mod-indexed) is dead simple to port to Go. The lowercase-vs-uppercase divergence from o2r is the only judgement call, and it was pre-decided in the issue. Rough edges / follow-ups worth noting, not filing yet: (1) I picked **last-`-`-segment** as the unmapped-org fallback (issue offered "raw org, or last segment") - if dozzle readability wants the raw org instead, that is a one-line flip. (2) Two concurrent containers pushed to `main` mid-run; my merges were clean auto-merges with no content conflicts, but it is a reminder that the naming-rewrite consumers (#387) should sync the JSON by a pinned ref, not a floating read.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os#302
No description provided.