Skip to content

feat: agent remote configuration (overlay, apply modes, safeguards) - #318

Closed
ccf-lisa[bot] wants to merge 43 commits into
mainfrom
lisa/in-flight-agent-config
Closed

ccf-lisa[bot] wants to merge 43 commits into
mainfrom
lisa/in-flight-agent-config

Conversation

@ccf-lisa

@ccf-lisa ccf-lisa Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Agent remote configuration on the Agents page. Users with agent:configure can:

  • see what each agent instance runs;
  • edit the agent's overlay inline;
  • review the per-instance outcome before saving;
  • browse history and revert.

The API is compliance-framework/api#465. Where the design and that code differ, this PR follows the code.

Important

Ship with or after compliance-framework/api#465.

  • This PR moves /admin/agents and its nav entry to agent:read.
  • The Configuration tab detects an older API (a 404 with no body on GET …/config) and shows an "unsupported" notice.

What's in it

Configuration tab

  • Header: the desired revision and a sync summary, with chips for instances that have problems.

  • Instance picker: stale instances are collapsed.

  • Per-instance notice:

    • the mode, and why a revision was rejected;
    • file warnings.
  • Views:

    • Effective: summary cards with provenance, or YAML;
    • File and Overlay: copy and download.
  • Plugins as tabs: one tab per plugin (ARIA tablist, arrow/Home/End keys, a scrollable strip for many plugins), so the selected plugin's card gets the full width. Tab labels show new / pending / removal / removed / disabled. Add plugin sits next to the strip and selects the new plugin's tab.

  • Inline editing on the Effective view: a ✏️ pencil on each editable field (source, schedule, enabled, labels, policy_data, config keys, verbosity, agent evidence, policy sources as OCI references or local paths). Row actions (edit, remove, reset, reorder) sit right next to the item they act on.

  • Three-state field access over the reporting instances (fresh and reported), mirroring the API's Classify + WillApply per instance:

    • all would apply the change: pencil only;
    • some would not: pencil + 🛡 shield, whose tooltip names the instances and why;
    • none would (e.g. all in report mode, no matching overridable_config_flags): read-only, with the reasons;
    • forbidden (🔒): api.*, daemon and remote_config.*, never changeable remotely.

    With no reporting instance yet, fields stay editable without a shield. Without agent:configure everything is read-only.

  • Add-plugin gating: the same three states for adding plugins.<name>: before a source is known, whether each instance could install a new plugin (apply_all; apply_safe with trusted_sources, or only by reusing a source its file already uses); in the dialog, the concrete source is classified per instance (already used / trusted / untrusted / local). None → the action (or the dialog's Add) is disabled. A new plugin's tab, card and fields keep the shield.

  • policy_data: a structured tree instead of a JSON blob: objects as collapsible groups, lists as lists, masked •••• values and ${env:NAME} references marked; deep or large containers start collapsed. Each key and list item has its own pencil / remove / add, and an edit keeps the value's JSON type. Type changes go through the Raw JSON view (a toggle). Both views record a minimal merge patch: only the changed pointers, null for a removed file key, a changed list written whole, and an entry is dropped when a value is set back to the file value. A list edit shows as one element change in the pending count, the inline markers, per-item undo and the review rows. Lists holding a masked value are locked in the tree (saving them would copy the mask).

  • Pending-changes bar: edits collect in a per-agent draft behind a sticky bar ("N pending changes · Review & save · Discard").

  • Advanced: edit the raw overlay as YAML, using the same review flow.

  • Review & save:

    • Per instance: a diff of merge(base, current) against merge(base, draft), with safety tags, a will-apply summary, and non-blocking file-origin warnings.
    • Save outcomes: 201, 200 (no-op), 409 (keep or discard my draft), 422, 403 and 413.
  • Secrets notice: placeholders such as ${env:NAME} are recommended for secrets, because overlays are readable by agent:read.

  • History: revisions with "Load more", view, diff against the previous or the current revision, and Revert.

  • Lib badge: each plugin card shows a neutral "agent vX" badge, the agent library its binary was built with (diagnostics only).

  • Layout: the Configuration tab has a single page scrollbar, and the pending-changes bar sticks to the bottom of the page scroll.

Validation

  • The API is the validator. The UI calls POST …/config/preview (debounced, 1.5 s). Its overlay errors and per-instance results appear at the matching fields.
  • Review & save is disabled while a preview is pending or reports errors. A failed live preview (network or 5xx) shows an error and a Retry button but doesn't disable Review, because Review runs its own preview.
  • Client-side checks cover only what the API can't see: YAML/JSON parsing (no aliases or << merge keys), masked •••• values (never sent), and boolean coercion in config/labels; numbers there must be quoted. The plugin-name pattern (O6) is also checked client-side, mirroring the API's PluginNamePattern.

Access (R40)

  • Route and nav: use agent:read. CRUD and keys are hidden unless the user has admin:manage, and keys are never fetched for non-admins.
  • Editing, saving and reverting need agent:configure.

Follows the API's security-review contract

  • An unset remote_config.mode is report by default (with credentials): the report-mode text says so, and the locked-keys panel shows it.
  • Masked values (••••) can appear under any key and at any depth; the UI recognises them by value everywhere.
  • Instance details no longer carry policy bundles: policyBundles, PolicyBundleReport and PolicyFileReport are removed; "Report truncated" now means the agent's file (base) was dropped.

Notable decisions

  • The editor drawer was removed. Its job is now done by inline editing and the raw YAML dialog (R69–R71).
  • Inline policy bundles are out of scope. The API no longer accepts policy_bundles, so there is no policy authoring UI; policies come from OCI or local sources.
  • Removed during the final review:
    • the client-side copies of the API's overlay rules;
    • the old plugin-library gate mirror;
    • the ?fixtures demo mode.

Testing

  • make reviewable (prettier, vue-tsc --build, eslint src/, Vitest: 173 files, 1528 tests) and make build pass. The unit suite ran 8× in a row with no unhandled errors (specs that render PrimeVue tabs now unmount after each test).
  • Visual check against the local-dev API (nothing saved): plugin tabs, field states, add-plugin gating, per-item policy_data edits (the API preview accepted them), element-level review rows, single page scroll.
  • End to end against api#465 on the local-dev RC stack (before inline bundles were removed): inline edits, review and save, and rejects across instances. Not re-run since the removal.

🤖 Generated with Claude Code

ccf-lisa Bot and others added 9 commits September 30, 2026 12:59
Types for the agent remote-configuration admin API, a single client
(useAgentConfigApi) with camelCase stop paths for the opaque snake_case
documents, If-Match writes and normalised errors, the jsonBody request
transform (R13), configure/configure-policy/sync actions and the
policy-author role (R40/R53), pure utilities (RFC 6901 pointers, RFC 7396
merge, overlay ops, provenance, diff, Go path.Match port, 5-field cron,
YAML via js-yaml CORE_SCHEMA, client-side validation, file states and the
policy-only check incl. R58), display vocabularies and an explicit
fixture mode. Adds js-yaml and the CodeMirror 6 packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a Configuration tab to the agents page: the desired revision, a
sync summary with problem chips, an instance picker (stale instances
collapsed), per-instance mode and rejection details, file warnings
(R41), and Effective (summary cards with provenance, or YAML), File and
Overlay views with copy/download. Provenance is computed against the
overlay of the revision each instance runs.

R40: the route and nav entry move to agent:read; registration, keys and
agent CRUD are hidden unless admin:manage, and keys are never requested
for non-admins. The tab mounts only while active (TabPanel is not lazy)
and waits for permission hydration before fetching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A right-side drawer edits the agent's overlay as one JSON Merge Patch
document, in a form or in YAML (CodeMirror 6), with debounced live
checks against the API preview (shields, trust hints), client-side
validation, and a review step: per-instance diffs of merge(base,
current) vs merge(base, draft) with safety tags, will-apply summary,
validated-only blocking (R48) and non-blocking file warnings (R59).
Saves use If-Match; 201/200 (R14), 409 (keep/discard), 422 (R6), 403
and 413 are handled. Clear overlay, a dirty-close guard, the policy-only
mode (U2.7) and the R57 notice recommending ${env:NAME} are included.

CodeMirror lives in async chunks; the Configuration tab and the editor
drawer are lazy too, so the AgentsView chunk barely grows. Fixture mode
loads its in-memory API on first use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a History view to the Configuration tab: revisions newest first
with "Load more" (totalPages, R49), author, relative time, comment,
size and Current / Revert-of badges; View and diffs (previous / current)
of the overlays, lazily loaded and cached per revision; and Revert
(agent:configure only) with an optional comment, handling 201, the 200
no-op, 409 (refresh + retry) and 422 (errors dialog).

Specs share a synchronous stand-in for the async CodeMirror components.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the Policies section to the editor: bundle cards with provenance,
age / origin and the "temporary" hint, Used-by wiring chips (whole-array
writes), New bundle (root-relative main.rego skeleton) and Customize a
bundle (extends + the R22 swap at the same index, sanitised and deduped
names), Delete / Reset, and a file table with the R17 states (inherited,
overridden, deleted, added, delete-missing, set, conflict) and actions
(override with the vendor package pre-filled, delete via the whole
delete array, undelete, restore, revert to vendor / drop file module,
add file, delete by path when the vendor list is unknown). Modules open
in a Rego CodeMirror editor with diagnostics merged from the preview,
the last 422 and — only when still accurate — the instance report.
Bundle data is edited as JSON, exclusive with a root data file (R18).
The read-only Effective view shows the same file states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Never copy or send the report mask: whole-object editors (policy_data,
  bundle data) omit untouched masked leaves and null removed file keys;
  the HTTP client refuses a PUT/preview containing "••••" (R25).
- Mirror the API's R59 rule client-side: problems already in an agent's
  file never block; a missing plugin source is only a hint standalone.
- Align checks with the API: case-insensitive CCF_API_AUTH_, robfig cron
  parsing (no trim, time zones), interval trim/negative, empty extends,
  unparsable data files, lossless-only YAML number coercion; guard
  pointer writes against "__proto__".
- R40: admin-only UI and the keys request wait for permission hydration.
- Removals (makeAbsent, undelete) consider every known instance file.
- Instance switching publishes detail and applied overlay together; stale
  loads are ignored; the File view is sanitized; provenance fallback is
  labelled.
- Editor: external CodeMirror replacements are not undoable (and module
  editors are keyed per file), close guards see unparsed YAML, conflict
  reload / Save disabled during a conflict, comment kept, fresh preview
  after "Keep my draft", stale 422 errors cleared, Customize defaults
  reset on reopen, policy-only reset gated, R58 hint for new extends.
- History: view errors, reload on new revisions, safe pagination.
- A11y: focusable, announced icon hints; live check status is polite-live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Validate the draft against the same instances the API validates a save
  against (fresh apply-mode, else the latest reported apply-mode one,
  else standalone) instead of every loaded base; the advisory
  policy-only check falls back to the same set.
- The plugin source check covers every effective plugin, so file plugins
  that already lack a source stay non-blocking (R59 baseline).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Review stays disabled until the instance files have loaded.
- The R58 hint for new bundle sources uses the validation bases, like
  the API's PolicyOnlyChange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 6296e088-35b4-40b0-9697-e332b7187a99

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at 6a149fa. The four inline threads are all non-blocking; one is a product question (NEEDS-DECISION).

Verified locally:

  • vue-tsc --build, eslint src/, prettier --check src/, Vitest (168 files, 1497 tests) and vite build all pass.
  • CI is green.
  • The client matches the contract in api#465 (agent_config.go):
    • stop paths and casing;
    • If-Match: "<rev>";
    • 200/201, 409 current-revision, 422 body keys read raw (not camelCased), 403, 413 and 428;
    • validated / R59 warnings treated as non-blocking;
    • PolicyOnlyChange parity, including R22 and R58;
    • totalPages.

Security checks:

  • No v-html; PrimeVue tooltips escape by default.
  • The mask is refused before PUT and preview.
  • __proto__ is handled.
  • Keys and admin UI wait for permission hydration.

Comment thread src/composables/agent-config/useAgentConfig.ts
Comment thread src/composables/agent-config/useAgentConfigApi.ts Outdated
Comment thread src/components/agents/config/editor/PolicySourcesEditor.vue Outdated
Comment thread src/components/agents/config/AgentConfigTab.vue Outdated
@ccf-lisa

ccf-lisa Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

PR approved. Marking ready for e2e.

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: R61 (policy-author Revert/Clear gating; owner decision); see thread

Comment thread src/components/agents/config/AgentConfigTab.vue Outdated
Revert and Clear overlay were configure-only in the UI while the API also
accepts configure-policy for policy-only changes (D18/R22/R58). Owner decision
R61: enable them whenever the API would accept the change.

- policyOnlyGate(bases, current, target) in utils/agent-config/policy-files.ts,
  shared by the history panel and the drawer (first non-policy path, else a
  generic R22/R58 reason).
- useAgentConfig.loadValidationBases(): validation-set bases (API
  ValidationBases), memoised per load, failing closed.
- AgentConfigHistory: canRevert -> revertAccess + currentOverlay + loadBases;
  per-revision decisions for the listed rows; openRevert guarded; 403 unchanged.
- AgentConfigEditorDrawer: Clear overlay gated by policyOnlyGate(validationBases,
  original, {}) for policy-only users.
@ccf-lisa

ccf-lisa Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

R61 is addressed in 8c873f0. The Divergences section of the PR body is now out of date: it says "Revert and Clear overlay require agent:configure in the UI (stricter than the API)", and that is no longer a divergence. Both controls are now enabled for configure-policy users whenever the change is policy-only (policyOnlyGate, which uses the same rule and the same validation bases as the API). lisa cannot edit the PR body, so a maintainer should please remove that item.

@ccf-lisa
ccf-lisa Bot requested a review from gusfcarvalho September 30, 2026 17:32
gusfcarvalho
gusfcarvalho previously approved these changes Sep 30, 2026

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R61 verified at 8c873f0 (incremental review of 6a149fa..8c873f0). This supersedes my REQUEST_CHANGES.

Checked:

  • policyOnlyGate wraps isPolicyOnlyChange and firstNonPolicyPath (with a generic fallback for R22/R58 refusals), so its result matches the API's D18 check in save().
  • Revert:
    • full is always enabled;
    • none is disabled;
    • policy-only is decided per revision against loadValidationBases(), which follows the API's ValidationBases and is memoised per load. It fails closed: disabled while checking, and on any detail or revision load error. openRevert is guarded by the same decision.
    • Decisions reset when the desired overlay or the role changes.
  • Clear overlay: configure users are always enabled; policy-only users go through policyOnlyGate(validationBases, original, {}) and are disabled while details load.
  • The 403 path is unchanged.
  • No regression for agent:configure users.

Local checks: vue-tsc, eslint, prettier, Vitest (168 files, 1510 tests) and vite build pass; CI is green; the lockfile is unchanged. I removed the obsolete Divergences item from the PR body.

ccf-lisa Bot added 5 commits October 1, 2026 08:08
… helpers (R62, R63, R64, R71)

Wire and pure helpers for the round-2 rework (design §13):
- R62: PolicyBundleReport / extends gain artifactDigest; the config client
  reads GET /api/artifacts/{digest}/files[/{path}] (bare bodies, per-segment
  path encoding); fixture mode serves vendor sources for its digests.
  vendorArtifactFor picks the digest (inline extends entry when it reports the
  same source, else the direct source report) and explains a miss.
- R63: PolicyError.code; POLICY_ERROR_CODE_LABELS for the contract,
  regocheck and agent codes; contractHints, a light line-based mirror of
  CheckContract for modules authored in the browser (advisory only).
- R64: the module template (title, description, violation, labels) replaces
  the bare package skeleton; vendorTestsFor finds the vendor tests of an
  overridden package; the vendor-test tooltip says compile failures reject.
- R71: fieldAccess derives forbidden / restricted / editable per pointer from
  each apply_safe instance's reported remote_config.
…e editor drawer (R69, R70, R71)

Owner e2e feedback (design §13): the 'Edit configuration' drawer was too
cramped and confusing with many plugins/policies.

- R69: one pending-changes draft per agent (draftRegistry), kept for the
  session and shared by every view of the agent. useConfigWorkspace binds
  it with the instance bases, live preview and permission rules. The
  Effective view gets a pencil per editable field (plugin source, enabled,
  schedule, protocol, policies, config keys, labels, policy_data,
  verbosity, agent_evidence.*) opening a small inline editor, plus
  Add/Remove plugin. A sticky bar shows 'N pending changes · Review & save ·
  Discard'; Review reuses the preview panel and saves ONE revision with
  If-Match and the 409 flow. Policy-only gating (R58/R61) disables Review.
- R70: the structured drawer, its form sections and specs are removed; an
  'Advanced: raw YAML' dialog edits the overlay and feeds the same draft.
  Clear overlay lives there, with the R61 gate.
- R71: editable (pencil), restricted (shield naming the apply_safe
  instances that will not apply it and why) and forbidden (lock, muted, no
  pencil: api.*, daemon, remote_config.*) states; the YAML dialog marks
  forbidden keys and blocks Apply (the API's locked-key 422 stays
  authoritative).

The Policies view these screens link to lands in the next commit.
…checks (R68, R62, R63, R64, R66)

R68: new route /admin/agents/:id/policies (agent:read; editing with
agent:configure or configure-policy), linked from the Configuration tab and
the bundle summary. A full-page workspace: inline bundles and the sources
plugins load | the bundle's file tree (inherited, overridden, added,
deleted, dropped; View / Override / Delete / Restore / Add file) | a
full-height CodeMirror editor and a validation panel running the preview.
Every change goes to the shared pending-changes draft (R69); the Configuration
tab and this view save together. Back links land on the agent's
Configuration tab (?agent=&tab=config).

R62/R64: Override fetches the current vendor file by artifact digest (the
inline entry's extends digest when it reports the same source, else the
direct source report) and pre-fills it. Without one (no digest, 404, or an
extends the agents have not reported) it confirms, then starts from the
module template instead of a body-less stub (§13.1). When vendor tests
cover the overridden package, it offers to delete them. View shows a vendor
file read-only.

R63: API policy errors show their code labels inline (markers) and in the
validation panel, next to browser contract hints (missing title, contract
keys as functions, literal types, …) for authored modules.

R66: Create a bundle from a source plugins use (or another source, or from
scratch) and Assign to plugins: for a plugin that loads the extended source
the default REPLACES it at the same index (R22, policy-only safe); adding
alongside is explicit and warns about duplicate evidence. Unassigning a
swapped bundle puts the source back.
@ccf-lisa

ccf-lisa Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Round 2: UI rework from the e2e feedback (design §13)

New head cd6edba (merged main first, in its own commit). Built against api#465 6c801a3 and agent#95 f88bde9.

What changed

  • R70: the editor drawer is gone. The drawer, its form sections and their specs are deleted. The Configuration tab now has Policies and Advanced: raw YAML in its header. The YAML dialog edits the overlay and feeds the pending draft. Clear overlay lives there and keeps the R61 gate.

  • R69: inline editing and one pending draft.

    • The Effective view has a ✏️ next to every editable field: plugin source, enabled, schedule, protocol, policies, each config key, labels, policy_data, verbosity and agent_evidence.*. There is also Add / Remove plugin.
    • Scalar fields get a small editor with Apply / Use file value / Agent default (R56). Maps, JSON and policy lists reuse the existing editors.
    • All edits, from the tab or the Policies view, go to one draft per agent and user (draftRegistry). It is kept in memory for the session and survives navigation between the two views.
    • A sticky bar shows N pending changes · Review & save · Discard, with per-change undo. Review reuses the preview panel and saves one revision with If-Match and the 409 flow. R58/R61 policy-only gating disables it.
  • R71: field states.

    • ✏️ editable.
    • 🛡 restricted: the tooltip names each fresh apply_safe instance that will not apply the change, and why (missing trusted_sources, overridable_config_flags or allow_inline_policies).
    • 🔒 forbidden: muted with no pencil (api.*, daemon, remote_config.*). The YAML dialog marks forbidden keys and blocks Apply. The API's locked-key 422 stays authoritative.
  • R68: Policies view at /admin/agents/:id/policies (needs agent:read; editing needs configure or configure-policy). It has three columns:

    • inline bundles and the sources plugins load;
    • a file tree showing inherited / overridden / added / deleted / dropped files, with View / Override / Delete / Restore / Add file;
    • a full-height CodeMirror editor and a validation panel that runs the preview.

    Create bundle starts from a source plugins use, another source, or from scratch. Assign to plugins is also here.

  • R62/R64: Override pre-fills the current vendor source.

    • It uses the inline entry's extends.artifact-digest when that report extends the same source; otherwise the direct source report's digest.
    • With no digest, a 404, or an extends mismatch, it asks to confirm and then starts from the module template (title, description, violation contains {…} if {…}, labels), never a body-less stub.
    • When vendor tests cover the overridden package, it offers to delete them.
    • View shows the vendor file read-only.
    • The tooltip now says vendor tests that no longer compile reject the revision.
  • R63 (UI): PolicyError.code shows as labelled markers in the editor, in the validation panel and in the review. Browser-side contract hints (missing title, contract keys defined as functions, literal types, …) cover authored modules. Add file uses the template.

  • R66: assigning a bundle that extends S replaces S at the same index by default (the R22 swap, which policy-only users may make). Adding it alongside is an explicit choice and warns about duplicate evidence. Unassigning puts S back.

Checks

  • vue-tsc --build, eslint, prettier --check, Vitest (172 files, 1528 tests) and vite build all pass.
  • New specs cover:
    • Override pre-fill, the 404 and no-digest fallbacks, and an extends mismatch;
    • the template and the vendor-test delete offer;
    • View and Add file;
    • contract markers and API codes in the validation panel;
    • the draft accumulating across the tab and the Policies view, saved as one revision;
    • the pending bar, review/save and 409;
    • inline pencil edits per field type, and Apply on an untouched field writing nothing;
    • forbidden and restricted states;
    • policy-only gating in the bar and the view;
    • swap-by-default, alongside, unassign and delete/undo;
    • the raw YAML dialog (forbidden keys, coercion, R61 Clear);
    • the route, and the ?agent=&tab=config deep link.

Bundle sizes (vite build, raw / gzip)

chunk before after
AgentsView 21.2 / 6.4 kB 21.7 / 6.6 kB
AgentConfigTab 126.5 / 41.3 kB 72.8 / 20.9 kB
AgentConfigEditorDrawer 86.2 / 26.2 kB removed
shared workspace (js-yaml, validation, inline editors) n/a 101.9 / 35.8 kB
AgentPoliciesView (route) n/a 56.5 / 18.4 kB
ReviewSaveDialog (async) n/a 19.8 / 6.8 kB
RawOverlayDialog (async) n/a 5.1 / 2.3 kB
CodeEditor / CodeMergeView (async) 46.4 / 29.3 kB unchanged

Deviations and notes

  • The module template uses "TODO: …" strings and a false violation body, so it parses and passes the contract checks as written. A literal { … } body would block the save.
  • Pending drafts live in memory, not in sessionStorage. A reload starts clean, and beforeunload warns first.
  • Restricted (🛡) only lists apply_safe restrictions. Report-mode and off-mode hosts are already called out by the instance notice.
  • Deferred (low): a refresh re-fetches every instance detail; contract hints are recomputed for every bundle on each keystroke; the Policies route ignores an in-place :id change (nothing in the UI navigates that way).

The decisions are recorded in the lisa-design git note.

@ccf-lisa
ccf-lisa Bot requested a review from gusfcarvalho October 1, 2026 11:55
gusfcarvalho
gusfcarvalho previously approved these changes Oct 1, 2026

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving round 2 at cd6edba (incremental review of 8c873f0..cd6edba, §13 R62–R71). Nothing is blocking; I left two non-blocking threads.

Checks: npm ci, vue-tsc --build, eslint, prettier, Vitest (172 files, 1528 tests) and vite build all pass. CI is green. The merge of main (#316) is clean: no conflict hunks, and it only touches ControlStatementMetadata.vue.

Verified:

  • Security:
    • No v-html / innerHTML in the new code. Vendor Rego, API and report text render as text or in CodeMirror, and tooltips escape.
    • "••••" is never sent:
      • inline text editors don't start from the mask and reject it as input;
      • map rows show it only as a placeholder;
      • policy_data and bundle data go through replacingPatch;
      • raw YAML Apply feeds a draft whose client issues block Review;
      • refuseMasked still guards PUT and preview.
    • New writes go through setAt/setOwn, so __proto__ stays safe.
    • Drafts are keyed by user id and agent id.
    • Forbidden keys: canEditPointer refuses them, raw YAML blocks Apply on locked keys, and client validation marks them as blocking.
  • R56: "file value" omits the key; "agent default", Auto and Remove write null (via makeAbsent); per-change undo goes through revertPointer. delete[] is written as the whole list, and the vendor-test delete offer adds the inherited tests the overridden package covers.
  • R62: Override uses extends.artifact-digest when the report extends the same source, else the direct source's digest. A miss, 404 or error goes to a confirm and then the template.
  • R58/R61: configure-policy users can only edit /policy_bundles/** and /plugins/*/policies. Review & save is gated by isPolicyOnlyChange over the validation bases. Clear overlay in the raw dialog keeps policyOnlyGate.
  • Regressions: none. The drawer is fully removed with no dead imports or routes. The new route is gated on agent:read. The 409 flow (keep / discard / reload) and 422/403/413 handling carry over to ReviewSaveDialog. Hydration is unchanged: ready follows ensurePermissions.

Deviations:

  • (a) The "TODO: …" template with a false violation body is acceptable. It parses and passes R63, the confirm explains it, and the evidence is visibly titled "TODO…". It does mean an untouched override always passes; consider a warning hint while the TODO markers remain.
  • (b) In-memory drafts with a beforeunload guard are acceptable. They survive in-app navigation and keep overlay edits out of browser storage.
  • (c) Listing only apply_safe reasons on the restricted shield matches R71; report-mode and off-mode hosts are covered by the instance notice.

Comment thread src/components/agents/config/editor/useBundleOps.ts Outdated
Comment thread src/views/admin/AgentPoliciesView.vue Outdated
gusfcarvalho added a commit that referenced this pull request Oct 5, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccf-lisa Bot pushed a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
Layer 1 of 21 in the stacked split of #318.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…AML helpers [2/21] (#323)

* feat(agent-config): JSON merge-patch, JSON pointer, overlay ops and YAML helpers

Layer 2 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): reject ambiguous YAML numbers and null roots; RFC 7396 provenance

- parseYaml rejects numbers that would not be saved as typed (.inf/.nan,
  0644/0x1F/0o17/+5, integers past 2^53) with the line and column, for
  every field and for mapping keys; quoting keeps them as text
- an explicit null / ~ root is not a mapping; only comment-only text is {}
- provenance: a null file value is present; under an array or scalar
  replacement, nulls are data and descendants are classified by the
  replacement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…g [3/21] (#324)

* feat(agent-config): agent config types, glob matching and cron parsing

Layer 3 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): time zone names are case-sensitive, as in Go

Go's time.LoadLocation reads zoneinfo files by name, case-sensitively on
the agent's host; Intl matches case-insensitively, so cron5 accepted
TZ=utc and CRON_TZ=europe/london, which the API and agent reject.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): type the preview's omitted-instances count

ConfigPreview.omittedInstances mirrors the API's configPreviewResponse
omitted-instances: the instances a bounded preview (50 instances /
16 MiB) left out. A save still validates against them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): type the paginated instance list's meta

InstancesMeta gains page, limit, total and totalPages (api#476/#483:
GET .../instances?page&limit, limit max 25). counts stay over every
instance. The fields are optional: an unpaginated API returned one page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
… [4/21] (#325)

* feat(agent-config): three-state field access over reporting instances

Layer 4 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): follow Classify for re-enabled plugins and reused sources

- apply_safe: re-enabling a plugin the host's file disables applies only
  with a trusted effective source, and re-checks the policies and ${env:}
  references it keeps (classify.go reenables-plugin)
- apply_safe without trusted_sources: reusing a source the file already
  uses is Safe, so `source` is partial instead of read-only
- usedSources skips disabled plugins, as the API does
- registry authorities with % are local sources (Go url.Parse)
- agentconfig-conformance.json: one table of expected results from the
  API's pkg/agentconfig tests, asserted against glob, cron5 and
  field-access so the copies cannot drift silently

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): forbidden-key tooltip text lives in tooltips.ts

FORBIDDEN_TOOLTIP reads TOOLTIPS['agents.config.field.forbidden'], per
docs/TOOLTIPS.md (UI-COMP-001).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agent-config): vendor the API's agentconfig conformance golden file

The conformance fixture is now the API's generated golden file
(pkg/agentconfig/testdata/conformance.json @ c0b3792), byte for byte,
instead of a hand-copied table (CORE-DUP-001).

- fixtures/README.md records the API commit and the update flow
- scripts/sync-agentconfig-conformance.sh [ref] copies it (npm run
  sync:agentconfig-conformance); --check compares it, passing with a
  notice while the API has not published the file
- CI job conformance-drift (pull-request.yml) runs the check against
  api@main; make reviewable does not (no network)
- .prettierignore keeps prettier from reformatting the vendored file
- the spec fails when the golden file gains a table it does not run

The golden file adds the case "re-enable keeps a local plugin source",
which field-access.ts already passes. The two case-mismatched time zones
it drops stay as UI-only cases in cron5.spec.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…rs [5/21] (#326)

* feat(agent-config): overlay validation, config diff and display helpers

Layer 5 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): block unstorable numbers; align large arrays by their common ends

- validateOverlayClientSide blocks Infinity, NaN and integers past 2^53
  anywhere in the overlay: JSON.stringify writes non-finite numbers as
  null, which RFC 7396 reads as deleting the key on every host
- arrayElementChanges matches the common prefix and suffix before the
  LCS, so one insertion in an array past the LCS limit is still one
  addition (regression test at 600 x 601)
- sanitizeForDisplay spec: client_secret removed from the copy only
- plugin-name cases join the agentconfig conformance table

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…[6/21] (#327)

* feat(agent-config): instance status classification and test fixtures

Layer 6 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): instance badge tooltips live in tooltips.ts

The one-shot, truncated-report and file-warnings badge tooltips read
TOOLTIPS['agents.config.instance.*'], per docs/TOOLTIPS.md (UI-COMP-001).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): sync summary from the API's fleet-wide counts

The instance list becomes paginated (api#476/#483), so the loaded rows
may be part of the fleet. summarizeSync takes meta.counts for the
fleet-wide numbers (total, in sync / expected, stale, not reporting)
and marks the summary partial when fewer rows are loaded than
counts.total: report-only (rows only) is then unknown, and the problem
chips cover the loaded rows. instancesMixed.meta carries the page
fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): a complete instance list keeps the row-based sync summary

The API counts sync status for every instance, stale ones included, so
taking inSync / expected from meta.counts showed an offline agent as
"In sync: 1/1". With every row loaded (the normal case, and always a
single instance) the summary is computed from the rows as before, with
only the total from counts; the counts are used only when the list is
partial, where they are the only fleet-wide numbers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
#328)

* feat(agent-config): policy_data model and minimal merge patches

Layer 7 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(agent-config): one storable-number check, shared with validation

policy-data.ts reuses validation.ts isStorableNumber and exports
hasUnstorableNumber for the raw policy_data view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): listInstances takes a page query

AgentConfigApi.listInstances(agentId, { page, limit }) follows the
paginated instance list (api#476/#483); InstancesList is one page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…bles (#329)

Layer 8 of 21 in the stacked split of #318.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
… review helpers [9/21] (#330)

* feat(agent-config): agent config API client, configure permission and review helpers

Layer 9 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): page through the agent's instances

- listInstances sends ?page&limit (default 1 and 25, the API's max) and
  returns one page with its meta (page, limit, total, totalPages and the
  fleet-wide counts)
- listAllInstances (instancePages.ts) is the one way to load every
  instance: pages in order until the last one, at most
  MAX_INSTANCE_PAGES (4 pages = 100 instances; a worst-case summary is
  about 3 MiB), each instance once. Past the cap, or when the list moved
  while paging, the result is marked partial

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): an instance list is partial only past the cap or when it moved

listAllInstances marks the result partial when it stops at the page
cap, or when a row comes back twice (the list moved under the pages, so
another row was skipped), instead of comparing the rows with
counts.total.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…21] (#331)

* feat(agent-config): agent config state and the editing workspace

Layer 10 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): edits wait until every instance's file is loaded

The draft decides null-vs-omit and "back to the file value" against
every known base. Editing was enabled before the background detail load
finished, and a failed detail load was skipped silently, so an edit
could drop an overlay entry another host's file still needs.

- canEditPointer is false while a reporting instance's detail is
  missing; basesBlockedReason says why (loading, or which hosts failed)
- failedBaseIds names the instances the last loadDetails could not
  load; loadDetails is the retry
- a single-instance agent's only file is the selected instance's, so it
  never waits on the background load (and makes no extra request)
- the workspaceHost test harness loads every detail, as the tab does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): load the instance list through the page helper; block edits on a partial fleet

- useAgentConfig loads instances with listAllInstances; it exposes
  instancesPartial and instanceTotal (meta.counts.total), and the sync
  summary takes the API's fleet-wide counts
- past the page cap, base-dependent edits are blocked through the
  existing gate (basesBlockedReason names the cap): field access and the
  draft would otherwise be computed over part of the fleet
- a single-instance agent still makes one list request and never waits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agent-config): the fake API serves instances page by page

helpers.ts pagedListInstances(items) answers listInstances as the
paginated API does (25 a page, page fields, counts over every item);
fakeApi uses it for the fixture fleet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* feat(agent-config): shared configuration UI pieces

Layer 11 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agent-config): the applied-instance notice case asserts the notice renders

"shows no rejection details for an applied instance" only asserted
absences, so it passed with no notice at all (CORE-TEST-002). It now
first expects the notice and its mode badge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
… picker [12/21] (#333)

* feat(agent-config): field editor building blocks, header and instance picker

Layer 12 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): editor tooltips live in tooltips.ts

KeyValueEditor's key-case warning and the "Reset to file value" tooltip
(KeyValueEditor, FieldHints) read TOOLTIPS['agents.config.*'], per
docs/TOOLTIPS.md (UI-COMP-001).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agent-config): page the instance picker; say when the header covers part of the fleet

- AgentInstancePicker renders 25 instances a page (fresh first, stale
  ones when shown) with Previous / Next, and opens on the selected
  instance's page
- AgentConfigHeader says "Showing N of M instances" when the loaded rows
  are part of the fleet; its numbers come from the API's counts
  (instanceCount is the fleet total)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): the header says when the in-sync ratio comes from fleet counts

On a partial instance list the in-sync ratio is the API's fleet-wide
counts, which include stale instances: the header now reads "In sync:
X/Y instances (incl. stale, from the API's fleet counts)". A complete
list shows the row-based ratio as before (2/4 for the fixture fleet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…1] (#334)

* feat(agent-config): save preview with diff rows and safety tags

Layer 13 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): show what the save preview left out

- the apply summary says how many instances the API's bounded preview
  omitted (omitted-instances): a save still validates against them
- 422 errors of instances without a preview panel (an omitted one, or
  one that reported after the preview) are listed under "Other
  instances": they block Save, so they must be visible

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
…lay dialogs [14/21] (#335)

* feat(agent-config): pending changes bar, review-and-save and raw overlay dialogs

Layer 14 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): raw overlay dialog asks before discarding edits

Escape no longer closes the dialog (inside the editor it only leaves
the editor), and Cancel or x ask before discarding text that differs
from what the dialog opened with: the tab unmounts the dialog on close,
so the edits would be lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
Layer 15 of 21 in the stacked split of #318.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* feat(agent-config): structured policy_data tree editor

Layer 16 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): raw policy_data JSON rejects numbers it cannot save as typed

JSON.parse reads 1e999 as Infinity, which would be saved as null (an RFC
7396 delete of the key), and rounds integers past 2^53; the raw view now
shows an error instead of applying them, like the structured add form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): policy_data lists warn when the files differ

A policy_data list is written whole (RFC 7396), so an edit gives every
instance the same list. Like the other editors (FieldHints), an
editable list whose value differs between the instances' files now says
"differs across instances" (useEditor().differsAcrossInstances, through
the tree context).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): keep the policy_data node's value chain intact

The "differs across instances" span sat between the container branch
and its v-else-if chain, starting a new chain: every container node
that did not differ also rendered the empty scalar value span. The
container branch is now one template holding the summary and the hint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* feat(agent-config): plugin summary card

Layer 17 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agent-config): raw policy_data view refuses 1e999 and 20-digit integers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): plugin card removal for any name, once every file is loaded

- pendingRemoval reads the draft's plugins as own properties (getOwn):
  a plugin named "constructor" (a valid name) read Object#constructor,
  so its removal showed no pending state and no Undo
- Remove is disabled, with the workspace's reason, until every
  reporting instance's file is loaded: removal nulls the plugin where
  any file has it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* feat(agent-config): plugin tabs and add-plugin dialog

Layer 18 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agent-config): plugin tabs by own name; Add plugin never replaces a defined plugin

- the tab's removal hint and the "Removed by overlay" card read plugins
  as own properties (getOwn), so a plugin named "constructor" is
  matched like any other
- Add plugin refuses every name the saved overlay or the draft defines
  (non-null), not only the shown cards and loaded files: adding writes
  the whole plugin and replaced a saved definition this instance does
  not run yet
- Add plugin waits, with the reason, until every reporting instance's
  file is loaded; the Effective view says files are loading, or names
  the ones that failed with a Retry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
Layer 19 of 21 in the stacked split of #318.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* feat(agents): Configuration tab on the Agents page

Layer 20 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(agents): the Configuration tab counts the whole fleet

The header's instance count is the API's fleet-wide total, not the
loaded rows. Tab specs cover one instance (one list request, no paging
notices) and a fleet past the page cap (fleet-wide header, paged
picker, edits blocked with the cap named).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
gusfcarvalho added a commit that referenced this pull request Oct 6, 2026
* test(agents): integration specs for the Configuration tab

Layer 21 of 21 in the stacked split of #318.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agents): the reader case asserts the tab renders before its absences

"a reader gets no pencils and no plugin actions" only asserted
absences, so it passed when the tab failed to load (CORE-TEST-002). It
now first expects the schedule field and the local-ssh card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agents): the synthetic apply_all instance has a loadable file

"keeps the shield on the new plugin's tab" lists an extra instance
(ip-all) the fixture API had no detail for. Its load failed silently
before; now Add plugin waits for every reporting instance's file, so
the spec serves ip-all's detail too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(agents): integration specs serve their instance lists page by page

AddPluginGating and InlineEditing build their fleets with
pagedListInstances, so meta (page fields, counts) matches the rows as
the paginated API's would.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: ccf-lisa[bot] <286799724+ccf-lisa[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant