From 977426fbb9cf2a6a9fcf25b37f8d7b177bcb7da4 Mon Sep 17 00:00:00 2001 From: petrsnd Date: Sat, 6 Jun 2026 15:27:57 -0600 Subject: [PATCH] docs: scrub internal-plan jargon (voyage, Phase N) from tracked files --- .agents/schemas/evidence.schema.json | 4 ++-- .agents/skills/safeguard-ps-operations/SKILL.md | 12 ++++++------ .agents/skills/script-authoring/SKILL.md | 2 +- AGENTS.md | 2 +- docs/agent-reference/README.md | 2 +- docs/agent-reference/samples-index.md | 2 +- samples/http/proxmox-ve-http/README.md | 2 +- tools/Build-SamplesIndex.ps1 | 4 ++-- tools/Invoke-PlatformDevLoop.ps1 | 2 +- tools/README.md | 2 +- tools/Test-AgentLinks.ps1 | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.agents/schemas/evidence.schema.json b/.agents/schemas/evidence.schema.json index ac257dc..d02d683 100644 --- a/.agents/schemas/evidence.schema.json +++ b/.agents/schemas/evidence.schema.json @@ -136,7 +136,7 @@ "definitions": { "probeRecord": { "type": "object", - "description": "A single probe step. Field shapes for 'observation' and 'destructiveDetails' are placeholders pending a real probing run; populated structures land in Phase 3 / Phase 5.", + "description": "A single probe step. Field shapes for 'observation' and 'destructiveDetails' are placeholders pending a real probing run; populated structures will land once the SSH and HTTP playbooks are authored against real targets.", "required": ["id", "kind", "command", "result"], "additionalProperties": false, "properties": { @@ -191,7 +191,7 @@ "enum": ["ok", "failed", "skipped", "halted"] }, "observation": { - "description": "TODO: structured observation payload. Shape locked down in Phase 3 once the SSH and HTTP playbooks are authored against real targets. Free-form for now.", + "description": "TODO: structured observation payload. Shape will be locked down once the SSH and HTTP playbooks are authored against real targets. Free-form for now.", "type": "object", "additionalProperties": true }, diff --git a/.agents/skills/safeguard-ps-operations/SKILL.md b/.agents/skills/safeguard-ps-operations/SKILL.md index 9a6ceea..7ceeb04 100644 --- a/.agents/skills/safeguard-ps-operations/SKILL.md +++ b/.agents/skills/safeguard-ps-operations/SKILL.md @@ -40,7 +40,7 @@ It is the **only** skill that directly calls `safeguard-ps`. Other skills reques Every cmdlet, parameter name, and parameter-set described to the operator MUST come from `Get-Help -Full` against the **installed** `safeguard-ps` module. Do not paraphrase from memory, vendor docs, or prior conversations. -Before invoking any cmdlet you have not used in the current voyage, the agent runs `Get-Help` itself — do not ask the operator to run it and paste output back: +Before invoking any cmdlet you have not used in the current session, the agent runs `Get-Help` itself — do not ask the operator to run it and paste output back: ```powershell Get-Help -Full | Out-String -Width 200 @@ -50,7 +50,7 @@ Get-Help -Full | Out-String -Width 200 If a cmdlet's parameter is a `[switch]` and the value comes from a variable, use the colon form (`-Insecure:$ins`, not `-Insecure $ins`). PowerShell silently swallows the latter on switches and the cmdlet ends up with the parameter's default — usually `$false` — which is rarely what the agent wanted. The value bound to the colon must be a `[bool]`. -Sibling cmdlets are not symmetric. `Test-` and `Invoke-` pairs diverge in parameter names; `New-SafeguardCustomPlatform` accepts `-ScriptFile` directly with no separate `Import-` step; `Get-SafeguardTaskLog` with no arguments returns a flat GUID array and only returns `{Recorded, Level, Event}` records when given `-TaskId `. Run `Get-Help` on every cmdlet's first use in the voyage, even when a sibling was just used. +Sibling cmdlets are not symmetric. `Test-` and `Invoke-` pairs diverge in parameter names; `New-SafeguardCustomPlatform` accepts `-ScriptFile` directly with no separate `Import-` step; `Get-SafeguardTaskLog` with no arguments returns a flat GUID array and only returns `{Recorded, Level, Event}` records when given `-TaskId `. Run `Get-Help` on every cmdlet's first use in the session, even when a sibling was just used. ## Authentication @@ -110,11 +110,11 @@ Do not pre-ask whether the appliance has a valid certificate. Try secure; the er ### Persist the session across iterations — serialize the token, never keep a long-running shell -**Login budget = 1 per voyage.** Each `Connect-Safeguard -DeviceCode` (or `-Browser`) costs the operator real time and attention. Connect exactly once. +**Login budget = 1 per session.** Each `Connect-Safeguard -DeviceCode` (or `-Browser`) costs the operator real time and attention. Connect exactly once. **Long-running interactive PowerShell sessions are banned in agent flows.** They wedge on PSReadLine prediction, swallow `$ConfirmPreference` prompts, return stale back-buffer through `read_powershell`, and routinely cost a re-login when the agent has to kill them. Do not start a persistent shell to hold `$Global:SafeguardSession`. Do not invoke `Connect-Safeguard` as an async command kept alive across iterations. -The only correct shape is short-lived sync `powershell -Command { ... }` calls. `$Global:SafeguardSession` holds **a short-lived bearer token, not a permanent credential** — valid for the rest of the voyage (typically several hours), safe to serialize to the gitignored per-session state directory, expires on its own. +The only correct shape is short-lived sync `powershell -Command { ... }` calls. `$Global:SafeguardSession` holds **a short-lived bearer token, not a permanent credential** — valid for the rest of the session (typically several hours), safe to serialize to the gitignored per-session state directory, expires on its own. **Step 1 — connect once and serialize, in the same process.** The connect call and the serialization step **must run in the same PowerShell process**: `$Global:SafeguardSession` dies with the process that called `Connect-Safeguard`, so a "connect in shell A, serialize in shell B" split silently throws the token away and burns a login. @@ -153,9 +153,9 @@ Two superficially-simpler shortcuts do **not** work. Documented here so the next If a future cmdlet is found that **only** reads the session variable and refuses `-AccessToken`, the correct response is to file a defect against safeguard-ps to add the parameter set — not to spin up a long-running shell to host the cmdlet. -Treat `sg-session.json` like any other secret: write it only under the per-session state directory, never commit it, never paste it into chat or task-log output. Delete it at the end of the voyage. The bearer token redacts itself naturally on expiry; a stale file cannot be used to attack the appliance later. Never log `$Global:SafeguardSession`, the access token, or any password parameter to operator-visible output. +Treat `sg-session.json` like any other secret: write it only under the per-session state directory, never commit it, never paste it into chat or task-log output. Delete it at the end of the session. The bearer token redacts itself naturally on expiry; a stale file cannot be used to attack the appliance later. Never log `$Global:SafeguardSession`, the access token, or any password parameter to operator-visible output. -If the agent finds itself about to call `Connect-Safeguard` a second time in the same voyage, **stop**. The token in `sg-session.json` is still good unless the operator rebooted the appliance or several hours have passed; re-read it. A second login is a defect, not a workaround. +If the agent finds itself about to call `Connect-Safeguard` a second time in the same session, **stop**. The token in `sg-session.json` is still good unless the operator rebooted the appliance or several hours have passed; re-read it. A second login is a defect, not a workaround. This pattern is verified in [`tools/README.md`](../../../tools/README.md) ("Authentication" section). `tools/Invoke-PlatformDevLoop.ps1` itself does not call `Connect-Safeguard`; the operator connects once and the wrapper picks up `$Global:SafeguardSession` (when invoked from a session that has it cached) or `-AccessToken` plumbed through. diff --git a/.agents/skills/script-authoring/SKILL.md b/.agents/skills/script-authoring/SKILL.md index 1a18753..cdc8802 100644 --- a/.agents/skills/script-authoring/SKILL.md +++ b/.agents/skills/script-authoring/SKILL.md @@ -177,7 +177,7 @@ The script shape is the same regardless of auth scheme: `BaseAddress` → `NewHt #### Pre-flight: rules every `Request` block must satisfy -These four rules each cost a real iteration on a prior voyage when violated. Check every `Request` block — including token-refresh and login calls inside helper functions — against all four before treating the draft as ready for local schema validation: +These four rules each cost a real iteration when violated in prior testing. Check every `Request` block — including token-refresh and login calls inside helper functions — against all four before treating the draft as ready for local schema validation: 1. **TLS skip is per-`Request`, not per-platform.** Declare the **reserved parameter** `SkipServerCertValidation` (`Type: Boolean`, `DefaultValue: false`) in every operation's `Parameters` and in any function `Parameters` array that issues a `Request`, then set `"IgnoreServerCertAuthentication": "%{SkipServerCertValidation}%"` on **every** `Request` block. SPP auto-sources the value from the asset's `VerifySslCertificate` flag — no `-CustomScriptParameters` plumbing at onboarding. Missing it on even one block (token refresh is the common miss) re-introduces TLS failure on that call only. 2. **Form bodies use `SetFormValue` + `Content.ContentObjectName`.** Never `Content.Value`. `Content.Value` is undocumented and the engine re-encodes — `%40` becomes `%2540`, `+` and `=` may be dropped — producing 400 BadRequest from targets that accept the identical body when sent manually. Mirror [`samples/http/twitter/CustomTwitter.json`](../../../samples/http/twitter/CustomTwitter.json) lines 133–148. diff --git a/AGENTS.md b/AGENTS.md index b3d0515..7b69df1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,7 +71,7 @@ The agent-facing index of every sample and template lives at [`docs/agent-refere Use this workflow when the operator's request is to build a custom platform that does not yet exist in the appliance. 1. **Gather requirements.** Classify intent (new vs enhance — this workflow is *new*), then collect what is missing: - - **Platform display name** the operator wants the new platform to use on the appliance (e.g., `My Custom Linux`). **Required pre-condition: do not hand off to `strategy-selection` or draft any JSON until the operator has supplied this verbatim.** If unspecified, ask before proceeding — never invent a name from the protocol, vendor, or strategy choice. Renaming a platform mid-voyage costs a re-import. + - **Platform display name** the operator wants the new platform to use on the appliance (e.g., `My Custom Linux`). **Required pre-condition: do not hand off to `strategy-selection` or draft any JSON until the operator has supplied this verbatim.** If unspecified, ask before proceeding — never invent a name from the protocol, vendor, or strategy choice. Renaming a platform mid-development costs a re-import. - Target system (vendor, product, version) and protocol (SSH or HTTP — telnet is out of scope). - **Operations needed.** SPP defines parallel operation families for each credential type: password (`CheckSystem`, `CheckPassword`, `ChangePassword`), SSH key (`CheckSshKey`, `ChangeSshKey`), API key (`CheckApiKey`, `ChangeApiKey`, `DiscoverApiKeys`), and file (`CheckFile`, `ChangeFile`), plus discovery operations (`DiscoverAccounts`, `DiscoverServices`, `DiscoverAssets`, `DiscoverSshHostKey`, `DiscoverAuthorizedKeys`). **Required pre-condition: do not hand off to `strategy-selection` or draft any JSON until the operator has confirmed the operation set verbatim.** Pick from the family that matches the credential intent — an API-key-only platform implements `CheckApiKey`/`ChangeApiKey` and nothing from the password family. Within a family, do not assume the full set: some platforms ship `Check*`-only at first, some skip `CheckSystem`, and every discovery operation requires an explicit yes. Each operation drafted but not requested is wasted iteration budget. - **Credential intent** — self-managed (the managed account rotates its own password) vs service-account (a separate account rotates the managed one). diff --git a/docs/agent-reference/README.md b/docs/agent-reference/README.md index dca7032..55b5eea 100644 --- a/docs/agent-reference/README.md +++ b/docs/agent-reference/README.md @@ -12,7 +12,7 @@ Human-facing documentation lives in `docs/concepts/`, `docs/guides/`, `docs/tuto | --- | --- | --- | | [`samples-index.md`](samples-index.md) | Normalized index of every sample and template (protocol, auth-scheme, operations, OS-family, file-path, README). | **Generated** by `tools/Build-SamplesIndex.ps1`. CI runs the same script with `-CheckOnly` and fails if the committed copy is stale. | | [`strategy-decision-tree.md`](strategy-decision-tree.md) | Decision table that backs the `strategy-selection` skill (SSH and HTTP only). | Hand-maintained from `docs/guides/`. SSH and HTTP only. | -| [`failure-patterns.md`](failure-patterns.md) | Error-signature → likely cause → fix catalog used by `task-log-analysis`. | **Empty in Phase 1.** Rows are populated from real extended task logs in Phase 5/F. Invented rows are not acceptable. | +| [`failure-patterns.md`](failure-patterns.md) | Error-signature → likely cause → fix catalog used by `task-log-analysis`. | **Initially empty.** Rows are populated from real extended task logs as failures are encountered. Invented rows are not acceptable. | | [`vendor-doc-search-recipes.md`](vendor-doc-search-recipes.md) | Query templates for fetching vendor docs and a normalization recipe for pasted vendor-doc excerpts. | Hand-maintained. | ## Related contracts diff --git a/docs/agent-reference/samples-index.md b/docs/agent-reference/samples-index.md index 0df4847..6494c9d 100644 --- a/docs/agent-reference/samples-index.md +++ b/docs/agent-reference/samples-index.md @@ -15,7 +15,7 @@ CI runs the same script with `-CheckOnly` and fails the build if the committed c - **protocol** — derived from the directory (`samples/ssh/`, `samples/http/`) or the template filename (`Pattern-GenericLinux*` / `Pattern-WindowsSsh*` / `TemplateSsh*` → ssh; `Pattern-GenericHttp*` / `Pattern-GenericRestApi*` / `TemplateHttp*` → http). - **auth-scheme** — best-effort from JSON content. HTTP: from `HttpAuth.Type`, an `Authorization: Bearer` header, an API-key-shaped custom header, or `ExtractFormData`. SSH: `Interactive` (Send/Receive), `Batch` (ExecuteCommand), or `Mixed`. Blank when undetermined. - **operations** — intersection of top-level keys with the canonical operation list from `schema/custom-platform-script.schema.json`. Imports and user-defined functions never appear here. -- **OS-family** — intentionally blank. Phase 1 prefers blank over guessed values; revisit in a later phase if needed. +- **OS-family** — intentionally blank. The build script prefers blank over guessed values; revisit once a reliable detection heuristic exists. - **file-path** and **README** — filesystem facts. `—` means the field could not be determined. - `samples/telnet/` is excluded — telnet is out of scope for the agent skill system. The samples remain in the repo for human reference. diff --git a/samples/http/proxmox-ve-http/README.md b/samples/http/proxmox-ve-http/README.md index 8af6f72..e54b620 100644 --- a/samples/http/proxmox-ve-http/README.md +++ b/samples/http/proxmox-ve-http/README.md @@ -68,7 +68,7 @@ No custom parameters. The full credential set comes from reserved parameters aut ## Limitations - **`@pve` realm only.** Users in `@pam` (the host's OS PAM stack) cannot be rotated via the Proxmox API regardless of API-level privileges — the Proxmox API call returns an error directing the caller to use `passwd` on the host. Rotating `@pam` users requires SSH-to-the-host + `sudo passwd `, which is a separate platform (not yet shipped). -- **Both self-managed and service-account modes are voyage-tested.** In service-account mode a privileged user (e.g. holding `PVEUserAdmin` on `/access/realm/pve`) rotates another `@pve` user's password. In self-managed mode the account rotates its own password; Proxmox always allows users to change their own password, so the `Realm.AllocateUser` privilege requirement does not apply. The same script handles both — SPP supplies the credentials such that `%FuncUserName%`/`%FuncPassword%` and `%AccountUserName%`/`%AccountPassword%` resolve to the same identity in self-managed mode, and the API accepts the `confirmation-password` field as benign on a same-user change. +- **Both self-managed and service-account modes are tested live against an appliance.** In service-account mode a privileged user (e.g. holding `PVEUserAdmin` on `/access/realm/pve`) rotates another `@pve` user's password. In self-managed mode the account rotates its own password; Proxmox always allows users to change their own password, so the `Realm.AllocateUser` privilege requirement does not apply. The same script handles both — SPP supplies the credentials such that `%FuncUserName%`/`%FuncPassword%` and `%AccountUserName%`/`%AccountPassword%` resolve to the same identity in self-managed mode, and the API accepts the `confirmation-password` field as benign on a same-user change. - **Ticket lifetime is ~2 hours.** Each operation fetches a fresh ticket; the script does not persist tickets across SPP operations. Self-rotation invalidates the ticket held during the change, but since the next operation re-authenticates, this is not observable. - **`401 Unauthorized` responses do not carry `WWW-Authenticate`.** Proxmox does not advertise the auth scheme on failure, which means generic HTTP debugging tools may misclassify the failure mode. The script does not rely on the header. - **No support for `DiscoverAccounts`.** The script does not enumerate Proxmox users; the operator adds managed accounts explicitly. diff --git a/tools/Build-SamplesIndex.ps1 b/tools/Build-SamplesIndex.ps1 index ce56067..8229dcb 100644 --- a/tools/Build-SamplesIndex.ps1 +++ b/tools/Build-SamplesIndex.ps1 @@ -29,7 +29,7 @@ SSH: "Interactive" if Send/Receive appear, "Batch" if ExecuteCommand appears without Send/Receive, "Mixed" if both, else blank. - - OS-family is left blank by the build script. Phase 1 prefers + - OS-family is left blank by the build script. The script prefers "blank" over "guess"; OS family is hard to ground from JSON alone and the sample README's heading text isn't structured. Future phases may revisit. @@ -284,7 +284,7 @@ CI runs the same script with `-CheckOnly` and fails the build if the committed c - **protocol** — derived from the directory (`samples/ssh/`, `samples/http/`) or the template filename (`Pattern-GenericLinux*` / `Pattern-WindowsSsh*` / `TemplateSsh*` → ssh; `Pattern-GenericHttp*` / `Pattern-GenericRestApi*` / `TemplateHttp*` → http). - **auth-scheme** — best-effort from JSON content. HTTP: from `HttpAuth.Type`, an `Authorization: Bearer` header, an API-key-shaped custom header, or `ExtractFormData`. SSH: `Interactive` (Send/Receive), `Batch` (ExecuteCommand), or `Mixed`. Blank when undetermined. - **operations** — intersection of top-level keys with the canonical operation list from `schema/custom-platform-script.schema.json`. Imports and user-defined functions never appear here. -- **OS-family** — intentionally blank. Phase 1 prefers blank over guessed values; revisit in a later phase if needed. +- **OS-family** — intentionally blank. The build script prefers blank over guessed values; revisit once a reliable detection heuristic exists. - **file-path** and **README** — filesystem facts. `—` means the field could not be determined. - `samples/telnet/` is excluded — telnet is out of scope for the agent skill system. The samples remain in the repo for human reference. diff --git a/tools/Invoke-PlatformDevLoop.ps1 b/tools/Invoke-PlatformDevLoop.ps1 index fd1e70c..4cd60fd 100644 --- a/tools/Invoke-PlatformDevLoop.ps1 +++ b/tools/Invoke-PlatformDevLoop.ps1 @@ -5,7 +5,7 @@ Run the custom-platform dev loop: validate -> import -> trigger -> fetch task lo .DESCRIPTION Wraps the agent-facing iterative loop into a single call that emits a single structured JSON document on stdout and exits with a phase-indexed exit code. -Designed to be called by the safeguard-ps-operations agent skill (Phase 3) and +Designed to be called by the safeguard-ps-operations agent skill and by humans during day-to-day authoring. Four modes, selected by mutually-exclusive switches: diff --git a/tools/README.md b/tools/README.md index 878d897..466d677 100644 --- a/tools/README.md +++ b/tools/README.md @@ -7,7 +7,7 @@ Tooling for the SafeguardCustomPlatform repo. | `TestTool.ps1` | Original human-facing upload + trigger script. Edit-in-place script with hard-coded variables. | Humans | | `Build-SamplesIndex.ps1` | Regenerates `docs/agent-reference/samples-index.md` from `samples/` and `templates/`. | CI + agents | | `Test-AgentLinks.ps1` | Validates relative links in `AGENTS.md` and `.agents/skills/*/SKILL.md` against `docs/agent-reference/`. | CI | -| `Invoke-PlatformDevLoop.ps1` | Structured dev-loop wrapper: validate → import → trigger → fetch task log. JSON output, phase-indexed exit codes. | Agents (Phase 3 `safeguard-ps-operations` skill) and humans | +| `Invoke-PlatformDevLoop.ps1` | Structured dev-loop wrapper: validate → import → trigger → fetch task log. JSON output, phase-indexed exit codes. | Agents (the `safeguard-ps-operations` skill) and humans | The remainder of this document covers `Invoke-PlatformDevLoop.ps1`. diff --git a/tools/Test-AgentLinks.ps1 b/tools/Test-AgentLinks.ps1 index a5f7b0b..0de90c1 100644 --- a/tools/Test-AgentLinks.ps1 +++ b/tools/Test-AgentLinks.ps1 @@ -18,7 +18,7 @@ - Paths are resolved relative to the markdown file that contains the link. - In-page anchors (links starting with '#') are skipped — anchor - validity is not in scope for Phase 1. + validity is not currently in scope. The script exits 0 on success, 1 on any broken link.