Skip to content

Step 4: credentials + post-to-site (Bluesky)#6

Merged
rockfordlhotka merged 2 commits intomainfrom
step-4-credentials
Apr 22, 2026
Merged

Step 4: credentials + post-to-site (Bluesky)#6
rockfordlhotka merged 2 commits intomainfrom
step-4-credentials

Conversation

@rockfordlhotka
Copy link
Copy Markdown
Member

Summary

Step 4 per spec §9.1 — credential broker + first capability that needs it.

  • Foragent.Credentials fleshed out: ICredentialBroker + CredentialReference(Id, Kind, Values) + CredentialNotFoundException. InMemoryCredentialBroker binds to a Credentials config section (user-secrets in dev — never appsettings). Per spec §6.3, in-memory broker is dev/test only; k8s-secrets broker deferred. Tenancy is single-tenant; per-tenant credential namespaces (§7.5) deferred.
  • IBrowserSession.OpenPageAsyncIBrowserPage — adds navigate / fill / click / wait-for-selector / get-url / get-text primitives so site posters can drive multi-step flows without depending on Microsoft.Playwright directly.
  • post-to-site capability (third on the agent card) dispatches to an ISitePoster keyed on the site input. BlueskySitePoster is the only implementation — drives the bsky.app web UI with app-password auth. Exception messages from posters are never echoed to callers (they could contain credential material); operators see the full exception in logs.
  • Spec invariants enforced: CredentialReference.ToString() scrubs values, Require() errors name missing keys but not values, PostToSiteCapability replaces the poster exception message with a generic failure string.
  • Deferred work captured in docs/framework-feedback.md step 4 so nothing gets lost: storage-state-as-credential §6.5, 2FA input-required flow §6.6, k8s-secrets broker §6.3, per-tenant credential namespaces §7.5, structured audit logging §7.4, domain allowlists §7.1.

Test plan

  • dotnet build --configuration Release — clean
  • dotnet test — 47/47 pass (34 unit + 12 browser integration + 1 smoke; 1 LLM-only integration test skipped when FORAGENT_LLM_* isn't set)
  • BlueskySitePosterIntegrationTests drives real Chromium against a Kestrel-hosted fake bsky.app-shaped login + compose UI
  • docker compose up --build + curl smoke test of post-to-site against a configured Bluesky credential (next in this session)

Known limitations

  • Selectors are exact strings, not regex. Playwright's string-selector dialect doesn't accept regex; BlueskySitePoster uses exact role=button[name="Sign in"]-style matches. Real-world bsky.app copy changes require a code update. Flagged in the framework-feedback doc.
  • Every post re-authenticates. Storage state persistence (spec §6.5) is deferred.
  • Credential ids via docker-compose / env vars must be single-segment. __ separates config path segments, so Credentials__rockbot__social__bluesky-rocky__Kind doesn't bind as an id. User-secrets / appsettings support arbitrary ids (slashes, etc.). Documented in CLAUDE.md.
  • Exception text from BlueskySitePoster is logged but scrubbed from A2A responses. The capability returns a generic "Post to bluesky failed." message; see logs for root cause.

🤖 Generated with Claude Code

rockfordlhotka and others added 2 commits April 21, 2026 18:27
Fleshes out Foragent.Credentials with ICredentialBroker +
CredentialReference(Id, Kind, Values), an in-memory broker bound to
a "Credentials" config section (dev/test only per spec §6.3), and
CredentialNotFoundException. Adds a third capability, post-to-site,
that resolves a credential by id and dispatches to an ISitePoster
keyed on the site input. BlueskySitePoster is the first (and only)
implementation, driving the bsky.app web UI with app-password auth.

IBrowserSession gains OpenPageAsync → IBrowserPage with navigate /
fill / click / wait-for-selector / get-url / get-text primitives so
the site poster can drive multi-step flows without taking a direct
dependency on Microsoft.Playwright.

Security invariants per spec §§6.1–6.2: credential values never
cross A2A boundaries, never land in logs, never embed in exception
messages. Post-to-site scrubs poster exception text before echoing
failures to callers — operators see the full exception in logs.

Deferred and tracked in docs/framework-feedback.md step 4:
storage-state-as-credential (§6.5), 2FA input-required flow (§6.6),
Kubernetes secrets broker (§6.3), per-tenant credential namespaces
(§7.5), structured audit logging (§7.4), domain allowlists (§7.1).

Tests (47 pass, 1 LLM-only skipped):
- InMemoryCredentialBroker unit tests + ToString-scrubs-values
- PostToSiteCapability unit tests with stub broker + poster
- BlueskySitePoster integration test: real Chromium against a
  Kestrel-hosted fake bsky.app login + compose UI

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real secret backends (k8s Secrets, cert stores, storage-state blobs)
are byte-native; forcing text at the broker boundary loses fidelity
for binary material. Switch Values to
IReadOnlyDictionary<string, ReadOnlyMemory<byte>>. Add FromText()
factory and RequireText() accessor for the common UTF-8 path so
text-origin callers don't encode by hand.

InMemoryCredentialBroker keeps text-native config binding (nobody
types base64 into user-secrets) and UTF-8 encodes at the boundary.

Also: document the three known gaps in the credential interface
(list, write, tenancy) in docs/framework-feedback.md so they don't
get rediscovered when the triggering capability lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rockfordlhotka rockfordlhotka merged commit 3168106 into main Apr 22, 2026
1 check passed
@rockfordlhotka rockfordlhotka deleted the step-4-credentials branch April 22, 2026 00:11
rockfordlhotka added a commit that referenced this pull request Apr 23, 2026
Ships the phase-1 / phase-3 form capability pair from spec §5.5 and
bumps the RockBot framework to 0.9.* for the multi-file skill API.

- FormSchema / FormField wire types with stable JSON serializer options.
- IBrowserPage.ScanFormAsync: single-JS-pass deterministic form read
  (labels, validation attrs, select/radio options). Adds
  SelectOptionAsync + SetCheckedAsync for the batch submit path.
- LearnFormSchemaCapability: navigate → deterministic scan →
  FormSchemaEnricher (one LLM turn, can only add dependsOn + notes;
  structural fields are DOM-authoritative) → persist as Skill with a
  SkillResourceType.JsonSchema "schema.json" resource at
  sites/{host}/forms/{slug}.
- ExecuteFormBatchCapability: resolves schema by skillRef via
  ISkillStore.GetResourceAsync or inline; streams per-row progress via
  AgentTaskContext.PublishStatus; default mode "abort-on-first"
  (spec open-question #8 resolution), caller opts into "continue".
  Success signal: optional successIndicator selector, URL-change
  fallback.
- Package bump 0.8.* → 0.9.* (adds RockBot.Llm.Abstractions). Docker
  image rockylhotka/rockbot-agent 0.8.5 → 0.9.11 so the peer side
  gets PR #291 structured-data invoke_agent.
- Version scheme adopted: 0.2.0-alpha.8 in Directory.Build.props,
  documented in CLAUDE.md Conventions.
- 14 unit tests + 1 Kestrel+Chromium end-to-end integration test.

Resolves spec open-questions #6 (typed artifacts — uses RockBot 0.9
resource files, no parallel Foragent store) and #8 (batch semantics —
abort-on-first default).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant