Design warded role capability tiers before adding ops #819

Closed
opened 2026-07-09 17:04:50 +00:00 by coilyco-ops · 4 comments
Member

Context: follow-up from ward#817 after the ops-role advisor answer and Kai's correction. The proposed warded ops role should not simply inherit existing write guardfiles. Before implementing ops, ward needs a clearer role capability taxonomy.

Problem:

  • Existing read/write/admin guardfile names are API-surface tiers, not agent-role postures.
  • director, engineer, advisor, and qa need observe surfaces plus limited forge coordination writes, such as issue comments/status/reservations/dispatch bookkeeping.
  • engineer needs repo-sandbox writes inside its owned feature container, but that is separate from live operational mutation.
  • ops should be the first role intentionally granted live operational write verbs.
  • Broad current aws, kubectl, or signoz write bundles would make ops unsafe if inherited directly.

Design ask:

  • propose capability categories for warded roles, for example observe/read, forge coordination write, repo-sandbox write, live-ops write, and admin/destructive
  • map current roles (director, engineer, advisor, qa) to those categories
  • map the proposed ops role to the minimal additional category set
  • identify which existing guardfiles or role bundles need renaming, splitting, or new tiers
  • keep ward#721 as the boundary guardrail: KDL owns guarded edge surfaces, ward owns role behavior and core runtime semantics
  • treat ward#503 / aos release-asset overlay as a dependency check, not a new implementation direction, unless the design finds an actual gap

Output: post a concrete design comment. If implementation follow-ups split cleanly, file them rather than trying to implement in the design pass.

Related: ward#817, ward#721, ward#503.

Context: follow-up from ward#817 after the ops-role advisor answer and Kai's correction. The proposed `warded ops` role should not simply inherit existing write guardfiles. Before implementing ops, ward needs a clearer role capability taxonomy. Problem: * Existing read/write/admin guardfile names are API-surface tiers, not agent-role postures. * `director`, `engineer`, `advisor`, and `qa` need observe surfaces plus limited forge coordination writes, such as issue comments/status/reservations/dispatch bookkeeping. * `engineer` needs repo-sandbox writes inside its owned feature container, but that is separate from live operational mutation. * `ops` should be the first role intentionally granted live operational write verbs. * Broad current `aws`, `kubectl`, or `signoz` write bundles would make ops unsafe if inherited directly. Design ask: * propose capability categories for warded roles, for example observe/read, forge coordination write, repo-sandbox write, live-ops write, and admin/destructive * map current roles (`director`, `engineer`, `advisor`, `qa`) to those categories * map the proposed `ops` role to the minimal additional category set * identify which existing guardfiles or role bundles need renaming, splitting, or new tiers * keep ward#721 as the boundary guardrail: KDL owns guarded edge surfaces, ward owns role behavior and core runtime semantics * treat ward#503 / aos release-asset overlay as a dependency check, not a new implementation direction, unless the design finds an actual gap Output: post a concrete design comment. If implementation follow-ups split cleanly, file them rather than trying to implement in the design pass. Related: ward#817, ward#721, ward#503.
Author
Member

🔎 ward agent advisor

ward agent advisor --harness codex ran a one-shot standard research pass on this question:

Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue.


Recommendation

Adopt a two-axis model and make ward own the role semantics.

  1. Axis 1: role capability categories in ward

    • observe - read-only access to repo state, Forgejo state, and explicitly read-only live surfaces.
    • forge-coordinate - limited Forgejo writes for agent orchestration: reservation comments, status/verdict comments, issue filing for fan-out, dispatch bookkeeping, and narrowly-defined issue state changes.
    • repo-sandbox-write - mutate the owned feature clone and granted writable repos inside the container. This is the engineer-only "carry code to merge" power, not a live-ops power.
    • live-ops-write - mutate live operational systems outside the repo sandbox.
    • admin-destructive - irreversible or high-blast-radius operations.
  2. Axis 2: guarded edge surfaces in ward-kdl

    • ward-kdl continues to own the raw guardfiles and bundles.
    • ward maps roles to the semantic categories above, and only then to the guardfile bundles that satisfy them.
    • That keeps the ward#721 boundary intact: KDL defines edges, ward defines role behavior.

This should be posted as the design direction, because the current model already proves the issue's concern: the fleet manifest documents advisor/director as holding a "live-observe" set, but the underlying guardfiles are not observe-only.

Why the current model is unsafe to extend

The repo already shows a category mismatch between role docs and actual powers:

  • ward-kdl.aws.guardfile.kdl includes writes like ssm put-parameter, s3 cp, and s3 sync.
  • ward-kdl.tailscale.guardfile.kdl includes set policy, create keys, and delete keys.
  • ward-kdl.kubectl.guardfile.kdl includes apply, scale, rollout restart, and rollout undo.
  • ward-kdl.signoz.guardfile.kdl includes create/edit operations for pipelines, dashboards, and rules.
  • Meanwhile docs describe advisor/director as holding a live-observe set, and agent_capability.go only interprets role capability as aws and tailnet booleans, not as semantic mutability.

So the main design problem is not just naming. It is that transport reach and operational mutability are currently conflated.

Recommended role mapping

  • advisor = observe + forge-coordinate
    • No repo-sandbox-write
    • No live-ops-write
    • If advisor needs live context, it may use observe-only live bundles, but only ones that are genuinely read-only.
  • qa = observe + forge-coordinate
    • Same posture as advisor, but narrower workflow purpose.
    • Default should stay no live-ops write.
  • director = observe + forge-coordinate
    • No repo-sandbox-write
    • No live-ops-write
    • Dispatching and stop/reconcile behavior belong to ward runtime semantics, not a broad operator-write bundle.
  • engineer = observe + forge-coordinate + repo-sandbox-write
    • This is the only current role that should mutate git state.
    • Its sandbox write power must stay clearly separate from live infrastructure mutation.
  • ops = observe + forge-coordinate + live-ops-write
    • Do not give it repo-sandbox-write by default.
    • Do not give it admin-destructive by default.
    • If an ops task also needs repo changes, that should still dispatch an engineer or be a separate explicit workflow.

That makes ops the first intentionally live-mutating role without turning it into a blended "engineer plus prod access" super-role.

What should be renamed, split, or added

  1. Do not reuse read/write/admin as role vocabulary.
    Those are API-surface tiers. Keep them as KDL/operator-surface terms only, or explicitly relabel them in docs/comments as surface tiers.

  2. Split the current so-called live-observe bundles into observe vs operate bundles.
    At minimum:

    • ward-kdl.aws.guardfile.kdl -> observe-only AWS bundle vs live-write AWS bundle
    • ward-kdl.tailscale.guardfile.kdl -> observe-only tailscale bundle vs live-write tailscale bundle
    • ward-kdl.kubectl.guardfile.kdl -> observe-only kubectl bundle vs live-write kubectl bundle
    • ward-kdl.signoz.guardfile.kdl -> observe-only signoz bundle vs live-write signoz bundle
  3. Introduce a Forgejo coordination bundle distinct from broad Forgejo write/admin.
    Current ward-kdl-{read,write,admin} is too broad for role attachment. A role-facing Forgejo bundle should cover orchestration writes only, not repo/release/org mutation.

  4. Keep eco-observe as the model.
    ward-kdl.eco-observe.guardfile.kdl is already shaped correctly: read-only commands, sealed where possible, explicitly no promote/apply/restart surface.

Concrete advisory decision

The clean design is:

  • ward owns the capability taxonomy and role-to-category mapping.
  • ward-kdl owns the concrete guardfiles that satisfy each category.
  • ops is the first role granted live-ops-write, but not repo-sandbox-write and not admin-destructive.
  • advisor/director/qa stay on observe + forge-coordinate only.
  • engineer stays the only role with repo-sandbox-write.

Options considered

  • Reuse existing read/write/admin tiers for roles - reject. They are API-surface tiers and already over-grant for the desired role semantics.
  • Push role semantics down into KDL - reject per ward#721. That inverts the boundary.
  • Two-axis model with ward semantic categories and KDL edge bundles - recommend.

Open questions to settle in follow-up implementation issues

  • What exactly belongs in forge-coordinate beyond comments and issue filing: close/reopen, labels, move-issue, reservation cleanup comment deletion, or only a smaller subset?
  • Should qa ever get observe-only live bundles by default, or should that stay advisor/director-only until a concrete need appears?
  • Does ops need a future optional repo-sandbox-write escape hatch for manifest repos, or should that remain a strict separate engineer dispatch forever? My recommendation is the latter.
  • How should the split bundles be named so they are obviously semantic, for example *-observe and *-operate, instead of another overloaded read/write pair?

Risks if ward adds ops before this split

  • A role documented as bounded live ops will silently inherit unrelated write powers from AWS, tailscale, kubectl, or Signoz bundles.
  • Future reviewers will continue reading live-observe in docs while the runtime grants mutation.
  • Once ops exists, backing out over-broad powers becomes harder because workflows will depend on them.

Dependency check on ward#503

I do not see a design blocker from ward#503. Treat it as a packaging check only: whatever bundle/guardfile names this taxonomy introduces must flow through the existing default-bundle and aos-overlay path cleanly. It does not argue for moving role semantics into the release asset overlay.

Bottom line

Post back that ward should define roles in terms of semantic capability categories - observe, forge-coordinate, repo-sandbox-write, live-ops-write, admin-destructive - and then split current guardfiles so the bundles attached to advisor/director/qa are truly observe-only. ops should then be introduced as the first role that adds live-ops-write, with repo-sandbox-write and admin-destructive still withheld.


Researched and posted automatically by ward agent advisor --harness codex (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Codex, via ward agent

### 🔎 ward agent advisor `ward agent advisor --harness codex` ran a one-shot **standard** research pass on this question: > Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue. --- **Recommendation** Adopt a **two-axis model** and make ward own the role semantics. 1. **Axis 1: role capability categories in ward** - `observe` - read-only access to repo state, Forgejo state, and explicitly read-only live surfaces. - `forge-coordinate` - limited Forgejo writes for agent orchestration: reservation comments, status/verdict comments, issue filing for fan-out, dispatch bookkeeping, and narrowly-defined issue state changes. - `repo-sandbox-write` - mutate the owned feature clone and granted writable repos inside the container. This is the engineer-only "carry code to merge" power, not a live-ops power. - `live-ops-write` - mutate live operational systems outside the repo sandbox. - `admin-destructive` - irreversible or high-blast-radius operations. 2. **Axis 2: guarded edge surfaces in ward-kdl** - ward-kdl continues to own the raw guardfiles and bundles. - ward maps roles to the semantic categories above, and only then to the guardfile bundles that satisfy them. - That keeps the [ward#721](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/721) boundary intact: **KDL defines edges, ward defines role behavior**. This should be posted as the design direction, because the current model already proves the issue's concern: the fleet manifest documents advisor/director as holding a "live-observe" set, but the underlying guardfiles are not observe-only. **Why the current model is unsafe to extend** The repo already shows a category mismatch between role docs and actual powers: - `ward-kdl.aws.guardfile.kdl` includes writes like `ssm put-parameter`, `s3 cp`, and `s3 sync`. - `ward-kdl.tailscale.guardfile.kdl` includes `set policy`, `create keys`, and `delete keys`. - `ward-kdl.kubectl.guardfile.kdl` includes `apply`, `scale`, `rollout restart`, and `rollout undo`. - `ward-kdl.signoz.guardfile.kdl` includes create/edit operations for pipelines, dashboards, and rules. - Meanwhile docs describe advisor/director as holding a **live-observe** set, and `agent_capability.go` only interprets role capability as `aws` and `tailnet` booleans, not as semantic mutability. So the main design problem is not just naming. It is that **transport reach and operational mutability are currently conflated**. **Recommended role mapping** - `advisor` = `observe` + `forge-coordinate` - No `repo-sandbox-write` - No `live-ops-write` - If advisor needs live context, it may use observe-only live bundles, but only ones that are genuinely read-only. - `qa` = `observe` + `forge-coordinate` - Same posture as advisor, but narrower workflow purpose. - Default should stay no live-ops write. - `director` = `observe` + `forge-coordinate` - No `repo-sandbox-write` - No `live-ops-write` - Dispatching and stop/reconcile behavior belong to ward runtime semantics, not a broad operator-write bundle. - `engineer` = `observe` + `forge-coordinate` + `repo-sandbox-write` - This is the only current role that should mutate git state. - Its sandbox write power must stay clearly separate from live infrastructure mutation. - `ops` = `observe` + `forge-coordinate` + `live-ops-write` - **Do not** give it `repo-sandbox-write` by default. - **Do not** give it `admin-destructive` by default. - If an ops task also needs repo changes, that should still dispatch an engineer or be a separate explicit workflow. That makes `ops` the first intentionally live-mutating role without turning it into a blended "engineer plus prod access" super-role. **What should be renamed, split, or added** 1. **Do not reuse `read/write/admin` as role vocabulary.** Those are API-surface tiers. Keep them as KDL/operator-surface terms only, or explicitly relabel them in docs/comments as **surface tiers**. 2. **Split the current so-called live-observe bundles into observe vs operate bundles.** At minimum: - `ward-kdl.aws.guardfile.kdl` -> observe-only AWS bundle vs live-write AWS bundle - `ward-kdl.tailscale.guardfile.kdl` -> observe-only tailscale bundle vs live-write tailscale bundle - `ward-kdl.kubectl.guardfile.kdl` -> observe-only kubectl bundle vs live-write kubectl bundle - `ward-kdl.signoz.guardfile.kdl` -> observe-only signoz bundle vs live-write signoz bundle 3. **Introduce a Forgejo coordination bundle distinct from broad Forgejo write/admin.** Current `ward-kdl-{read,write,admin}` is too broad for role attachment. A role-facing Forgejo bundle should cover orchestration writes only, not repo/release/org mutation. 4. **Keep `eco-observe` as the model.** `ward-kdl.eco-observe.guardfile.kdl` is already shaped correctly: read-only commands, sealed where possible, explicitly no promote/apply/restart surface. **Concrete advisory decision** The clean design is: - **ward** owns the capability taxonomy and role-to-category mapping. - **ward-kdl** owns the concrete guardfiles that satisfy each category. - **`ops` is the first role granted `live-ops-write`, but not `repo-sandbox-write` and not `admin-destructive`.** - **advisor/director/qa stay on `observe` + `forge-coordinate` only.** - **engineer stays the only role with `repo-sandbox-write`.** **Options considered** - **Reuse existing `read/write/admin` tiers for roles** - reject. They are API-surface tiers and already over-grant for the desired role semantics. - **Push role semantics down into KDL** - reject per [ward#721](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/721). That inverts the boundary. - **Two-axis model with ward semantic categories and KDL edge bundles** - recommend. **Open questions to settle in follow-up implementation issues** - What exactly belongs in `forge-coordinate` beyond comments and issue filing: close/reopen, labels, move-issue, reservation cleanup comment deletion, or only a smaller subset? - Should `qa` ever get observe-only live bundles by default, or should that stay advisor/director-only until a concrete need appears? - Does `ops` need a future optional `repo-sandbox-write` escape hatch for manifest repos, or should that remain a strict separate engineer dispatch forever? My recommendation is the latter. - How should the split bundles be named so they are obviously semantic, for example `*-observe` and `*-operate`, instead of another overloaded `read/write` pair? **Risks if ward adds `ops` before this split** - A role documented as bounded live ops will silently inherit unrelated write powers from AWS, tailscale, kubectl, or Signoz bundles. - Future reviewers will continue reading `live-observe` in docs while the runtime grants mutation. - Once `ops` exists, backing out over-broad powers becomes harder because workflows will depend on them. **Dependency check on ward#503** I do not see a design blocker from [ward#503](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/503). Treat it as a packaging check only: whatever bundle/guardfile names this taxonomy introduces must flow through the existing default-bundle and aos-overlay path cleanly. It does **not** argue for moving role semantics into the release asset overlay. **Bottom line** Post back that ward should define roles in terms of **semantic capability categories** - `observe`, `forge-coordinate`, `repo-sandbox-write`, `live-ops-write`, `admin-destructive` - and then split current guardfiles so the bundles attached to advisor/director/qa are truly observe-only. `ops` should then be introduced as the first role that adds `live-ops-write`, with `repo-sandbox-write` and `admin-destructive` still withheld. --- Researched and posted automatically by `ward agent advisor --harness codex` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Codex, via `ward agent`
Owner

Axis 1: role capability categories in ward

I'm honestly of the opinion that ward proper should get out of the business of directly governing roles in such a direct way. For example, I want end users to be able to compose a "devops" role via a configuration driven mix of "engineer" and "ops" capabilities. That moves us in the opposite direction of what is described, BUT only as far as ward is concerned. aos can and should adopt this level of clarity

terminology:

  • observe => read? default?
  • forge-coordinate => project-management
  • repo-sandbox-write => engineering
  • live-ops-write => ops
  • admin-destructive => admin

guarded edge surfaces in ward-kdl

agree

role breakdowns:

  • advisor: read
  • qa: read
  • engineer: read, engineering
  • director: read, project-management
  • ops: read, ops (duh)

^ these breakdowns will require a bunch of adjustments all over the place. And maybe also they aren't the right breakdowns? Pressure that the breakdown for me. But from the point of view of their semantic names as they relate to, for example, a real saas company.

There might be an "ops read" tier missing here, conceptually, R&D / product department would get but others (sales, etc...) would not. But all of these roles right now are R&D / product, and advisor is short of board member esque role.

Do not reuse read/write/admin as role vocabulary.

agree

Split the current so-called live-observe bundles into observe vs operate bundles.

agree

Introduce a Forgejo coordination bundle distinct from broad Forgejo write/admin.

I think the bundle you are reading for is an "admin" special case / keyworded bundle that dynamically aggregates all the other bundle permissions (and optionally overlays them). This bundle is for humans to use.

> Axis 1: role capability categories in ward I'm honestly of the opinion that ward proper should get out of the business of directly governing roles in such a direct way. For example, I want end users to be able to compose a "devops" role via a configuration driven mix of "engineer" and "ops" capabilities. That moves us in the opposite direction of what is described, BUT only as far as ward is concerned. aos can and should adopt this level of clarity terminology: - observe => read? default? - forge-coordinate => project-management - repo-sandbox-write => engineering - live-ops-write => ops - admin-destructive => admin > guarded edge surfaces in ward-kdl agree role breakdowns: - advisor: read - qa: read - engineer: read, engineering - director: read, project-management - ops: read, ops (duh) ^ these breakdowns will require a bunch of adjustments all over the place. And maybe also they aren't the right breakdowns? Pressure that the breakdown for me. But from the point of view of their *semantic names* as they relate to, for example, a real saas company. There might be an "ops read" tier missing here, conceptually, R&D / product department would get but others (sales, etc...) would not. But all of these roles right now are R&D / product, and advisor is short of board member esque role. > Do not reuse read/write/admin as role vocabulary. agree > Split the current so-called live-observe bundles into observe vs operate bundles. agree > Introduce a Forgejo coordination bundle distinct from broad Forgejo write/admin. I think the bundle you are reading for is an "admin" special case / keyworded bundle that dynamically aggregates all the other bundle permissions (and optionally overlays them). This bundle is for humans to use.
Author
Member

🔎 ward agent advisor

ward agent advisor --harness codex ran a one-shot standard research pass on this question:

Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue.


Recommendation

Adopt a composable capability taxonomy and treat the current named roles as default presets, not as the only hard-coded role model.

The thread's correction is the key design change from the earlier advisor answer: ward should not bake a rigid director|engineer|advisor|qa|ops -> permission mapping too deeply into product logic. Instead:

  • ward should own the semantic capability vocabulary, composition rules, and runtime behavior that follows from those capabilities
  • ward-kdl should continue to own the guarded edge surfaces and their concrete bundles
  • aos / the default fleet bundle can ship the opinionated default role breakdown for advisor, qa, engineer, director, and the proposed ops
  • end users should be able to compose a future role like devops from the same capability set without having to fork ward core semantics

That keeps the ward#721 boundary intact: KDL defines and constrains edge surfaces, while ward defines the runtime meaning of capabilities and how roles compose them.

Use semantic names, not the existing read/write/admin tier names:

  • read - observe state only. Repo reads, Forge reads, and genuinely read-only live surfaces.
  • project-management - limited forge coordination writes for workflow control: reservation bookkeeping, issue comments, status/verdict comments, labels/mode bookkeeping, dispatch-related issue filing, and similar non-code coordination.
  • engineering - mutate the owned repo sandbox and granted writable repos inside an engineer container. This is git/worktree power, not live infra power.
  • ops - mutate live operational systems outside the repo sandbox.
  • admin - destructive or high-blast-radius powers. Human-oriented special case by default, not part of the normal agent-role baseline.

These names match the semantic direction Kai proposed in-thread and read more like real organizational capability buckets than transport tiers.

Default role mapping

Recommended default presets:

  • advisor = read
  • qa = read
  • director = read + project-management
  • engineer = read + engineering
  • ops = read + ops

Two deliberate exclusions matter:

  • ops should not implicitly get engineering
  • ops should not implicitly get admin

That keeps ops from becoming a blended super-role with both prod mutation and repo-carry authority. If an ops workflow also needs code changes, the clean model is still a separate engineer dispatch or an explicitly composed custom role.

Why the current model is not safe enough to extend directly

The repo already shows the mismatch the issue is worried about:

  • cmd/ward/agent_capability.go reduces role capability to two booleans, aws and tailnet, derived from guardfile membership
  • .ward/ward-kdl/ward-kdl.fleet.kdl currently grants both advisor and director the so-called live-observe set: ward-kdl.aws.guardfile.kdl and ward-kdl.tailscale.guardfile.kdl
  • but those underlying surfaces are not observe-only

Concrete examples from the current generated guardfile docs:

  • docs/ward-kdl/ward-kdl.aws.guardfile.md includes ssm put-parameter, s3 cp, and s3 sync
  • docs/ward-kdl/ward-kdl.tailscale.guardfile.md includes policy set, keys create, and keys delete
  • docs/ward-kdl/ward-kdl.kubectl.guardfile.md includes apply, scale, rollout restart, and rollout undo
  • docs/ward-kdl/ward-kdl.signoz.guardfile.md includes pipeline/dashboard/rule create and edit operations

So the problem is deeper than naming. Today, transport reach and operational mutability are still conflated.

What to rename, split, or add

1. Keep read/write/admin as surface-tier language only

Do not reuse those names as the role taxonomy. They already mean something else in ward-kdl: compile-time operator-surface tiers.

2. Split live surfaces into semantic observe vs operate bundles

Before introducing ops, split the broad monoliths that currently mix observation and mutation. At minimum, the surfaces that an ops or live-observe-style role might touch need a read/operate split:

  • AWS
  • Tailscale
  • kubectl
  • SigNoz
  • any similar live operational surface added later

Naming should be semantic and explicit, e.g. *-read and *-ops, or *-observe and *-operate. The exact suffix is less important than ending the overloaded current state.

3. Add a narrow forge coordination bundle distinct from broad Forgejo write/admin

The current issue asks for limited forge coordination writes, not general forge mutation.

That suggests a dedicated project-management / forge-coordination bundle for things like:

  • reservation comments
  • verdict/status comments
  • issue filing for fan-out
  • mode/triage labels
  • dispatch bookkeeping
  • possibly close/reopen if ward workflow needs it

This should stay distinct from broad Forgejo write/admin surfaces.

4. Treat admin as a special aggregate, not the default agent path

Kai's thread note about an admin keyworded bundle that aggregates other permissions makes sense as a human/operator special case. That should remain clearly separate from the default role taxonomy used for normal warded agents.

The clean shape is:

  1. ward defines capabilities semantically
  2. fleet config/default bundle maps default roles to those capabilities
  3. ward-kdl provides the concrete edge bundles that satisfy each capability
  4. custom deployments can compose new roles from capabilities

That satisfies both sides of the thread:

  • ward still owns runtime semantics, per ward#721
  • ward proper does not overfit itself to one fixed roster forever

Options considered

Option A. Reuse current read/write/admin tiers as role vocabulary

Reject. Those are surface tiers already, and they overgrant for the proposed role semantics.

Option B. Keep role semantics implicit and just add ops on top of current guardfiles

Reject. The current live surfaces already mix observation and mutation too broadly. Adding ops before splitting them would institutionalize unsafe inheritance.

Option C. Put role semantics down into ward-kdl

Reject per ward#721. That inverts the desired boundary.

Option D. Define semantic capabilities in ward, ship default role presets, and let deployments compose roles from them

Recommend. This matches the thread correction and preserves composability.

Open questions to settle next

  • What exactly belongs in project-management beyond comments and labels: close/reopen, assignment, reservation cleanup, issue creation only, or more?
  • Should there eventually be a distinct live-read subcategory for non-R&D audiences, or is plain read sufficient for the current warded roster?
  • Should ops ever be combined with engineering in one default preset, or should that stay an explicit deployment-level composition only? Recommendation: explicit composition only.
  • What naming is clearest for split live bundles: read/ops, observe/operate, or similar?

Risks if ward adds ops before this taxonomy split

  • A role documented as bounded live operations will silently inherit unrelated mutating powers from existing AWS, Tailscale, kubectl, or SigNoz bundles.
  • Reviewers will continue reading terms like "live-observe" while the actual granted surfaces still mutate live systems.
  • Backing broad powers out later will be harder once workflow assumptions form around them.

Dependency check on ward#503

ward#503 looks like historical packaging context, not the design driver here. In the current tree, docs/release.md says the old build-time aos overlay path is superseded by live config resolution and no longer the active release direction. So the useful dependency check is simply:

  • any renamed or split bundles still need to flow cleanly through the current embed/config-source/package publication path

There is no evidence here that ward#503 should change the role-capability design itself.

Bottom line

Post the design direction as:

  • semantic capabilities: read, project-management, engineering, ops, admin
  • default role presets: advisor=read, qa=read, director=read+project-management, engineer=read+engineering, ops=read+ops
  • composition principle: defaults ship in the bundle, but deployments can compose new roles from the same capabilities
  • required prerequisite before ops lands: split current live bundles so observe and mutate surfaces are no longer conflated

That is the clearest way to make ops safe without hard-coding ward into a permanently closed role model.


Researched and posted automatically by ward agent advisor --harness codex (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Codex, via ward agent

### 🔎 ward agent advisor `ward agent advisor --harness codex` ran a one-shot **standard** research pass on this question: > Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue. --- ## Recommendation Adopt a **composable capability taxonomy** and treat the current named roles as **default presets**, not as the only hard-coded role model. The thread's correction is the key design change from the earlier advisor answer: ward should not bake a rigid `director|engineer|advisor|qa|ops` -> permission mapping too deeply into product logic. Instead: - **ward** should own the **semantic capability vocabulary**, composition rules, and runtime behavior that follows from those capabilities - **ward-kdl** should continue to own the **guarded edge surfaces** and their concrete bundles - **aos / the default fleet bundle** can ship the opinionated default role breakdown for `advisor`, `qa`, `engineer`, `director`, and the proposed `ops` - end users should be able to compose a future role like `devops` from the same capability set without having to fork ward core semantics That keeps the `ward#721` boundary intact: KDL defines and constrains edge surfaces, while ward defines the runtime meaning of capabilities and how roles compose them. ## Recommended capability categories Use **semantic names**, not the existing `read/write/admin` tier names: - **`read`** - observe state only. Repo reads, Forge reads, and genuinely read-only live surfaces. - **`project-management`** - limited forge coordination writes for workflow control: reservation bookkeeping, issue comments, status/verdict comments, labels/mode bookkeeping, dispatch-related issue filing, and similar non-code coordination. - **`engineering`** - mutate the owned repo sandbox and granted writable repos inside an engineer container. This is git/worktree power, not live infra power. - **`ops`** - mutate live operational systems outside the repo sandbox. - **`admin`** - destructive or high-blast-radius powers. Human-oriented special case by default, not part of the normal agent-role baseline. These names match the semantic direction Kai proposed in-thread and read more like real organizational capability buckets than transport tiers. ## Default role mapping Recommended default presets: - **`advisor`** = `read` - **`qa`** = `read` - **`director`** = `read` + `project-management` - **`engineer`** = `read` + `engineering` - **`ops`** = `read` + `ops` Two deliberate exclusions matter: - `ops` should **not** implicitly get `engineering` - `ops` should **not** implicitly get `admin` That keeps `ops` from becoming a blended super-role with both prod mutation and repo-carry authority. If an ops workflow also needs code changes, the clean model is still a separate engineer dispatch or an explicitly composed custom role. ## Why the current model is not safe enough to extend directly The repo already shows the mismatch the issue is worried about: - `cmd/ward/agent_capability.go` reduces role capability to two booleans, `aws` and `tailnet`, derived from guardfile membership - `.ward/ward-kdl/ward-kdl.fleet.kdl` currently grants both `advisor` and `director` the so-called live-observe set: `ward-kdl.aws.guardfile.kdl` and `ward-kdl.tailscale.guardfile.kdl` - but those underlying surfaces are **not observe-only** Concrete examples from the current generated guardfile docs: - `docs/ward-kdl/ward-kdl.aws.guardfile.md` includes `ssm put-parameter`, `s3 cp`, and `s3 sync` - `docs/ward-kdl/ward-kdl.tailscale.guardfile.md` includes `policy set`, `keys create`, and `keys delete` - `docs/ward-kdl/ward-kdl.kubectl.guardfile.md` includes `apply`, `scale`, `rollout restart`, and `rollout undo` - `docs/ward-kdl/ward-kdl.signoz.guardfile.md` includes pipeline/dashboard/rule create and edit operations So the problem is deeper than naming. Today, **transport reach and operational mutability are still conflated**. ## What to rename, split, or add ### 1. Keep `read/write/admin` as surface-tier language only Do **not** reuse those names as the role taxonomy. They already mean something else in ward-kdl: compile-time operator-surface tiers. ### 2. Split live surfaces into semantic observe vs operate bundles Before introducing `ops`, split the broad monoliths that currently mix observation and mutation. At minimum, the surfaces that an `ops` or live-observe-style role might touch need a read/operate split: - AWS - Tailscale - kubectl - SigNoz - any similar live operational surface added later Naming should be semantic and explicit, e.g. `*-read` and `*-ops`, or `*-observe` and `*-operate`. The exact suffix is less important than ending the overloaded current state. ### 3. Add a narrow forge coordination bundle distinct from broad Forgejo write/admin The current issue asks for limited forge coordination writes, not general forge mutation. That suggests a dedicated **project-management / forge-coordination** bundle for things like: - reservation comments - verdict/status comments - issue filing for fan-out - mode/triage labels - dispatch bookkeeping - possibly close/reopen if ward workflow needs it This should stay distinct from broad Forgejo write/admin surfaces. ### 4. Treat `admin` as a special aggregate, not the default agent path Kai's thread note about an `admin` keyworded bundle that aggregates other permissions makes sense as a **human/operator special case**. That should remain clearly separate from the default role taxonomy used for normal warded agents. ## Recommended architectural shape The clean shape is: 1. **ward defines capabilities semantically** 2. **fleet config/default bundle maps default roles to those capabilities** 3. **ward-kdl provides the concrete edge bundles that satisfy each capability** 4. **custom deployments can compose new roles from capabilities** That satisfies both sides of the thread: - ward still owns runtime semantics, per `ward#721` - ward proper does not overfit itself to one fixed roster forever ## Options considered ### Option A. Reuse current `read/write/admin` tiers as role vocabulary Reject. Those are surface tiers already, and they overgrant for the proposed role semantics. ### Option B. Keep role semantics implicit and just add `ops` on top of current guardfiles Reject. The current live surfaces already mix observation and mutation too broadly. Adding `ops` before splitting them would institutionalize unsafe inheritance. ### Option C. Put role semantics down into ward-kdl Reject per `ward#721`. That inverts the desired boundary. ### Option D. Define semantic capabilities in ward, ship default role presets, and let deployments compose roles from them Recommend. This matches the thread correction and preserves composability. ## Open questions to settle next - What exactly belongs in `project-management` beyond comments and labels: close/reopen, assignment, reservation cleanup, issue creation only, or more? - Should there eventually be a distinct **live-read** subcategory for non-R&D audiences, or is plain `read` sufficient for the current warded roster? - Should `ops` ever be combined with `engineering` in one default preset, or should that stay an explicit deployment-level composition only? Recommendation: explicit composition only. - What naming is clearest for split live bundles: `read/ops`, `observe/operate`, or similar? ## Risks if ward adds `ops` before this taxonomy split - A role documented as bounded live operations will silently inherit unrelated mutating powers from existing AWS, Tailscale, kubectl, or SigNoz bundles. - Reviewers will continue reading terms like "live-observe" while the actual granted surfaces still mutate live systems. - Backing broad powers out later will be harder once workflow assumptions form around them. ## Dependency check on `ward#503` `ward#503` looks like **historical packaging context, not the design driver here**. In the current tree, `docs/release.md` says the old build-time aos overlay path is superseded by live config resolution and no longer the active release direction. So the useful dependency check is simply: - any renamed or split bundles still need to flow cleanly through the current embed/config-source/package publication path There is no evidence here that `ward#503` should change the role-capability design itself. ## Bottom line Post the design direction as: - **semantic capabilities**: `read`, `project-management`, `engineering`, `ops`, `admin` - **default role presets**: advisor=`read`, qa=`read`, director=`read+project-management`, engineer=`read+engineering`, ops=`read+ops` - **composition principle**: defaults ship in the bundle, but deployments can compose new roles from the same capabilities - **required prerequisite before `ops` lands**: split current live bundles so observe and mutate surfaces are no longer conflated That is the clearest way to make `ops` safe without hard-coding ward into a permanently closed role model. --- Researched and posted automatically by `ward agent advisor --harness codex` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Member

Consult interrogation: the design pass is complete and the first carrying implementation issue is #846, covering composable semantic role capabilities. Closing this consult shell; #817 remains the later ops-role destination.

Consult interrogation: the design pass is complete and the first carrying implementation issue is #846, covering composable semantic role capabilities. Closing this consult shell; #817 remains the later ops-role destination.
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/ward#819
No description provided.