feat(scripts): sync fetcher entry scripts to Paramify + associate to evidence sets#3
Open
21tmccauley wants to merge 3 commits into
Open
feat(scripts): sync fetcher entry scripts to Paramify + associate to evidence sets#321tmccauley wants to merge 3 commits into
21tmccauley wants to merge 3 commits into
Conversation
…evidence sets
Adds the paramify_scripts uploader and a `paramify scripts sync` command that
pushes each fetcher's entry file (fetcher.py/fetcher.sh) to the new Paramify
/scripts API and CONNECTs it to that fetcher's evidence set, so the tenant shows
how the evidence was generated.
The /scripts API has no stable external key and no server-side versioning, so
the tool reconciles the tenant to the repo (GitOps):
- identity: a marker in the script `description` (`paramify-fetcher: <name>`),
indexed client-side from GET /scripts (no server name filter exists).
- versioning: the fetcher.yaml `version` is the update signal; a sha256 of the
entry file guards undeclared drift (warn+skip by default, --force to push).
- association: get-or-create the evidence set by reference_id (shared identity
with paramify_evidence), then POST /evidence/{id}/associate CONNECT SCRIPT.
Only SCRIPT is automated; solcap/control/validator linkage stays manual.
Separate from evidence upload (provisioning, run on fetcher change). Shared
modules are intentionally ignored — only the entry file is pushed.
- uploaders/paramify_scripts/{uploader.py,uploader.yaml,README.md}
- framework/api.py: scripts_sync + scripts_sync_preflight facade
- framework/cli.py: `paramify scripts sync` (--dry-run/--force/--reassociate/--json)
- tests/test_scripts_uploader.py: marker roundtrip, create/update/drift/noop,
drift gate, association, --reassociate, https guard, error isolation
- CLAUDE.md: document the command + uploader
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Surfaces `paramify scripts sync` in the TUI by folding it into the (renamed) Paramify tab alongside evidence upload — the two share token, base_url, overrides config, and the event-stream shape, so they share the log/worker/ preflight machinery. - rename tab 5 "Upload" -> "Paramify" (id unchanged) - UploadPage: "Sync Scripts" button + dry-run/force/reassociate checkboxes, a ScriptsSyncEvent worker, and sync_start/sync_item/sync_complete rendering - the Scripts action is repo-scoped: gated on scripts_sync_preflight (token + fetchers), independent of the run picker; dry-run runs read-only without a confirm, a real sync confirms first and requires the token - rebuild() now shows EVIDENCE and SCRIPTS readiness sections side by side - extend the CLI/TUI parity harness to know the `scripts` command group, symmetric with `manifest` (EXPECTED_SCRIPTS + API_TO_CLI entries) Verified via a Textual pilot: Paramify tab composes, widgets resolve, Sync Scripts enables on 107 fetchers with no token (dry-run path). Full suite: 91 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Collaborator
Author
|
Added TUI support (commit 5fe7e0b): folded
Verified with a Textual pilot (tab composes, widgets resolve, button enables on 107 fetchers in the no-token dry-run path). Full suite: 91 passed. |
…squat) `paramify tui` told users to run `pip install 'paramify[tui]'`, but this project is not published to PyPI and its distribution name is `paramify-fetchers`, not `paramify`. The bare name resolves to an unrelated PyPI package (`paramify`), which pip installs instead — clobbering the editable install and still leaving textual absent. Point at the editable local install from the repo root: `pip install -e '.[tui]'`, and warn off the PyPI form. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
paramify scripts synccommand (backed by a newuploaders/paramify_scripts/uploader) that uses the new v0.6.0/scriptsAPI to push each fetcher's entry file to Paramify and CONNECT it to that fetcher's evidence set — so the tenant shows how each piece of evidence was generated.Provisioning step, separate from
paramify upload: run it whenfetchers/**change, not on every collection.Design (why it's a reconcile, not a create)
The
/scriptsAPI gives us{id, name, description, code}with no stable external key (only a server UUID) and no server-side versioning. So the tool reconciles the tenant to the repo, GitOps-style:description:GET /scripts(there is no server-side name/marker filter).fetcher.yaml versionis the update signal; git is history-of-record; the app holds "current".--forceto push.reference_id(same identityparamify_evidenceuses, honoring the sameoverridesconfig), thenPOST /evidence/{id}/associateCONNECT SCRIPT. Idempotent-tolerant;--reassociatere-drives every fetcher to heal a partial failure.Decisions baked in (per discussion)
fetcher.py/fetcher.sh; shared modules ignored.evidence_set.name.SCRIPTassociations are automated; solution-capability / control-implementation / validator linkage stays Paramify-side.Actions per fetcher
create(new) ·update(version bumped) ·drift(code changed, version didn't — warn/skip,--force) ·noop.Files
uploaders/paramify_scripts/{uploader.py,uploader.yaml,README.md}framework/api.py—scripts_sync+scripts_sync_preflightfacade (mirrors the evidence uploader loader)framework/cli.py—paramify scripts sync(--dry-run/--force/--reassociate/--json)tests/test_scripts_uploader.py— 8 tests (marker roundtrip, action decisions, drift gate, association-on-change,--reassociate, https guard, per-fetcher error isolation)CLAUDE.md— documents the command + uploaderTesting
--dry-runplans all 107 fetchers aswould_create(discovery + entry-file read + marker planning exercised without touching the API).Not yet run against a live tenant (needs
PARAMIFY_UPLOAD_API_TOKEN). Please runparamify scripts sync --dry-run(read-only) against your tenant to confirm the plan, then a real sync on a couple of fetchers. One assumption to verify on that first real call: thatPOST /evidence/{id}/associateCONNECTis idempotent / tolerant of an already-connected script (the API has no pre-check andGET /evidence/{id}doesn't expose associations, so the client treats analready/exists/connected400/409 as success).Follow-ups (out of scope)
paramify_evidence+paramify_scripts(kept self-contained here to avoid destabilizing the tested evidence uploader).🤖 Generated with Claude Code