feat: add strandly-harness — one opinionated Strands agent (CLI / MCP / AgentCore)#75
Conversation
… / AgentCore) Imports the Strandly harness from mkmeral/strandly-harness into its new home. Included: the harness package (src/), full test suite (581 tests, AWS/network-free), CDK infra app, maintainer dashboard, AGENTS.md design doc. Excluded for now (follow-ups): docs/, presentation/, CI/CD workflows (deploy/invoke), and internal research notes under the code-review skill. Adjustments for the new home: - README.md rewritten to match devtools package style - OIDC default trust repo updated to strands-agents/devtools - removed tests/test_workflows.py (guards the excluded workflows) - root README: added Tools section entry
|
@strandly-the-agent can you review this PR? it's a devtools PR so we don't have to be strict with all nits |
|
@strandly-the-agent can you review the PR? trying it for second time, hope you there lol |
|
@strandly-the-agent I am getting sad now, because you are ignoring me 😢 |
|
TL;DR — Genuinely solid, well-tested import (✅ 581 tests pass, ✅ ruff clean, ✅ secret sweep clean, ✅ infra IAM 🟡 Owner allow-list bypass for a nested plain-string repo target. Trace, impact, fix direction, other findings & what I verifiedTrace (against validate_owner(
{"input": {"branch": {"repositoryNameWithOwner": "attacker/evil-repo", "branchName": "main"},
"message": {"headline": "x"}, "expectedHeadOid": "…",
"fileChanges": {"additions": [{"path": "x", "contents": "eA=="}]}}},
allowed_owners={"strands-agents", "strands-labs"}, is_mutative=True, strict=True, token="…",
query="mutation($input: CreateCommitOnBranchInput!){ createCommitOnBranch(input:$input){commit{url}} }",
) # -> (None, None) i.e. NOT blocked
Impact (calibrated, not alarmist): bounded by the token's push scope, and supervised deployments still have HITL/Cedar gating by tool name. But this allow-list is exactly the repository-scope layer meant to constrain a broadly-scoped token (per the module docstring & Fix direction (yours to design): (a) have Other findings
What I verified (branch
Caveat: my sandbox session dropped mid-review, so the |
…flows Sync the vendored strandly-harness with the latest upstream (mkmeral/strandly-harness): - 2 new skills (implement, release-notes), first-response references, mention audit log, sandbox git-credential bootstrap, config secret-ARN region fallback, prompt compose updates, dashboard + infra updates. Suite grows 581 -> 678 tests. Re-applied open-sourcing adaptations (same policy as the initial import): - OIDC trust defaults point at strands-agents/devtools - internal research-notes dossiers excluded; pointers patched - private prior-art docstring generalized - scrubbed a real AWS account id + secret suffix from a new test fixture (replaced with the 111122223333 documentation id) New repo workflows (.github/workflows/): - strandly-ci.yml: path-filtered lint+test for strandly-harness/** (uv locked install, ruff, pytest, actionlint, advisory mypy; py3.10 + py3.12) - strandly-deploy.yml: provision + deploy the AgentCore runtime on push to main touching strandly-harness/** (and manual dispatch), keyless via the OIDC deploy role - strandly-invoke.yml: invoke the deployed runtime via workflow_dispatch / reusable workflow_call / maintainer-gated @strandly mentions (prompt via env, never inlined; session id derived from the GitHub context like every other ingress) tests/test_workflows.py ported and extended: guards the role split, script-injection rules (now covering mention bodies), session derivation, and the mention gate.
Summary
Imports the Strandly harness (from mkmeral/strandly-harness) into its new home as a
strandly-harness/folder in devtools — now synced with the latest upstream and wired with repo workflows for CI, deploy, and invocation.Strandly is an autonomous agent that helps build Strands Agents, built with Strands itself — one opinionated agent (Opus on Bedrock, fixed tools/plugins/prompt) served three ways: CLI, MCP server, and Bedrock AgentCore Runtime. Every capability is secret-gated with a local fallback, so the identical code runs on a laptop and as a hosted runtime. It currently powers @strandly-the-agent.
What's included
src/strandly_harness/build_agentfactory, tools, plugins, 8 skills, memory, serving surfacesinfra/dashboard/tests/FakeModelAGENTS.md.github/workflows/strandly-*.ymlGitHub workflows (new in this revision)
All three are path-filtered to
strandly-harness/**and keyless (OIDC roles minted by the harness's ownOidcStack, deploy/invoke role split — the invoke role can never redeploy):strandly-ci.yml—uv sync --locked(supply-chain gate) → actionlint → ruff → pytest → advisory mypy, on py3.10 + py3.12.strandly-deploy.yml— deploys Strandly on code changes: push tomaintouchingstrandly-harness/**(+ manual dispatch) drivesprovision → deploy → runtime-iamwith the privileged deploy role (trust locked to this repo'smain/production). Regeneratesrequirements.txtfromuv.lockso the AgentCore image builds from exact pins.strandly-invoke.yml— invokes the deployed Strandly on events:workflow_dispatch(one-shot prompt), reusableworkflow_call, or a maintainer-gated@strandlymention on an issue/PR (body must contain@strandlyAND author must be OWNER/MEMBER/COLLABORATOR and not a bot). Prompt/mention bodies are passed via env, never inlined (script-injection guard); the session id is derived from the GitHub context by the same helper the mention poller uses, so an Action invoke threads into the same AgentCore Memory session as every other ingress.tests/test_workflows.pypins these invariants in CI: role split, noAWS_DEPLOY_ROLE_ARNin invoke, no user-controlled text inlined inrun:blocks, session derivation, and the mention gate.Deliberately excluded
docs/andpresentation/(internal design docs / talk material)references/*-research.md— they cite private-repo issues throughout); the shipped references (pipeline.md,taxonomy.md) and role prompts are self-contained, and pointers to the removed files were patched outPre-open-source security review
Full sweep on the initial import (source + all git history) and re-swept on this upstream sync — clean:
AKIA…), PEM blocks, GitHubghp_/ghs_tokens, Slack/OpenAI/Anthropic keys,password=etc. — no hits.envgitignored;uv.lockpublic PyPI hashes onlyinfra/is least-privilege (scoped ARNs)111122223333documentation id here (flagged upstream too)Adjustments for the new home
README.mdin devtools package style, plus a short section documenting the two runtime workflowsREADME.md: Tools section entry forstrandly-harnessmkmeral/strandly-harness→strands-agents/devtools(overridable via CDK context)plugins/goal.py: private prior-art docstring pointer generalizedtests/test_workflows.py: repointed at the rootstrandly-*.ymlworkflows and extended (mention bodies covered by the injection guard; new mention-gate test)Validation (run in the new location)
Requested by @mkmeral in agent-of-mkmeral/strands-coder-private#381.