Skip to content

Derive report utilization's per-profile override refusal from HMCConfig fields #1891

Closed

Description

@randomparity

Problem

hmcpctl report utilization refuses exported HMC_* variables that would override every profile's own connection settings, but the list is a hand-maintained tuple. #1743 was caused by exactly this drift (two TLS fields missing), and nothing fails when a new per-profile field is added. Other per-profile fields, such as HMC_SSH_KEY_FILE, are neither refused nor documented as global.

Evidence

  • src/hmcpctl/cli_commands/report.py:101-110 (_PROFILE_OVERRIDES), :398-403.
  • src/hmcpctl/config.py declares 21 HMCConfig fields; a profile may set ssh_key_file (config.py:1074).
  • docs/cli.md:608-614 lists the refused names and says HMC_TIMEOUT and HMC_SCHEMA_VERSION still apply to every profile.

Expected

Every HMCConfig field is classified as per-profile (refused when exported) or global (documented), and a test fails when a new field is added without a classification.

Proposed approach

Add the explicit classification next to the tuple and a test comparing it with HMCConfig.model_fields. Decide the remaining fields (ssh_key_file, ssh_verify_host_key, agent_id, ...) and update docs/cli.md.

Dependencies

None.

Verification and boundaries

Focused CLI tests, just verify. No live HMC access is needed; the change is verifiable with focused tests and just verify.

Provenance

Filed by campaign 601fc41ce62e-1e90b4bf-a0bf-4a80-b3ac-c93b2596b8de from #1743 (security pass and branch review notes 1 and 3) follow-up review, against main at 3bc63ff3. The operator authorized filing all deduplicated follow-up candidates as unparented issues on 2026-11-04.

Activity

  1. added this to the 0.7.0 milestone on Nov 4, 2026
  2. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    SCOPE CHECKPOINT — report override classification

    Issue #1891, campaign 152593d7a535-fdfc969e-824d-4d69-a9ae-3ce95496903a; step 1, before frozen scope/design. No branch, PR, implementation or live HMC access. Source inspected against a96d5db8428f6fc41ca9e8113ba417b4578306ef; the relevant files are unchanged from the inspected checkout.

    The report currently refuses eight environment overrides (src/hmcpctl/cli_commands/report.py:101) and explicitly documents only timeout and schema_version as report-global (docs/cli.md:608). All 21 fields flow through environment-over-profile precedence (src/hmcpctl/config.py:962). A read-only AST comparison of the declared fields against the refused tuple plus the two documented globals fails with 11 unclassified fields. No full guardrails ran because implementation has not started.

    Proposed smallest policy: retain the two documented globals, preserve the eight existing refusals, and classify the eleven uncovered fields as per-profile/refused. This is a proposal requiring an operator decision, not an inferred authorization. The table accounts for every current field:

    Field Proposed classification Existing authority/evidence
    host Per-profile; retain refusal Report tuple and docs/cli.md:608; config.py:211
    port Per-profile; retain refusal Same; config.py:212
    user Per-profile; retain refusal Same; config.py:213
    password Per-profile; retain refusal Same; config.py:214
    ssh_key_file Per-profile; new refusal Credential path, config.py:215; issue explicitly identifies omission
    ssh_verify_host_key Per-profile; new refusal SSH host trust, config.py:218; no report-global contract
    verify_ssl Per-profile; retain refusal Report tuple and docs; config.py:221
    ca_bundle Per-profile; retain refusal Report tuple and docs; config.py:224
    cert_fingerprint Per-profile; retain refusal Report tuple and docs; config.py:233
    trust_system_store Per-profile; retain refusal Report tuple and docs; config.py:243
    timeout Global; retain allowance Explicit report contract, docs/cli.md:613; config.py:252
    upload_timeout Per-profile; new refusal Upload timing, config.py:253; no report-global contract
    max_response_bytes Per-profile; new refusal REST response bound, config.py:259; client/core.py:744 enforces it
    ssh_timeout Per-profile; new refusal SSH timing, config.py:264; no report-global contract
    audit_memento Per-profile; new refusal Per-profile audit header, config.py:269; client/core.py:561
    schema_version Global; retain allowance Explicit report contract, docs/cli.md:613; config.py:277
    agent_id Per-profile; new refusal Attribution/header, config.py:289; environment-variables.md:333 distinguishes exported from profile attribution
    authorize_power_operations Per-profile; new refusal Ownership guard, config.py:300; environment-variables.md:137 documents profile scope
    iso_url_allowlist Per-profile; new refusal Download trust policy, config.py:314; no report-global contract
    iso_build_entry Per-profile; new refusal Executable selection, config.py:325; no report-global contract
    iso_build_arguments Per-profile; new refusal Build arguments, config.py:335; no report-global contract

    Decision needed: approve this exact 19-per-profile / 2-global partition, or specify which of the eleven new refusals should instead be documented report-global. Refusing fields unused by this read-only survey is stricter for existing shells; allowing audit/agent or response-limit overrides makes their fleet-wide effect an explicit public contract. General advice to export agent identity does not by itself settle the report-specific exception. SSH credentials and trust are not proposed as globals.

    Approved exclusions remain unchanged: general profile precedence, configuration schema and other commands are outside this issue and owned by the operator; global-option diagnostic improvements belong to #1892. The operator's 2026-11-04 “Approve all displayed scopes” approved that exclusion set, but did not settle these field-specific policies. #1892 stays serial after #1891 merges. No new issue was filed.

    Resume after the field-policy decision: freeze scope, validate the exact optional reservation docs/adr/0338-report-config-override-classification.md=0338, create the sibling worktree, then perform full design and implementation. Advisory classification: non-trivial, full-spec, complexity S / fixed denominator 100 changed lines, iterating review, one PR; hazards are environment/configuration precedence, credential trust and public override behavior. No review rounds consumed and no review findings or follow-up candidates yet.

  3. added
    status:needs-humanPipeline cannot proceed; human must diagnose
    and removed on Nov 5, 2026
  4. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    Policy approval — resume step 1

    The operator explicitly approved “Approve 19 refused / 2 global” for the exact 21-field table in #1891 (comment). Only timeout and schema_version remain global; the other nineteen fields are per-profile/refused, including the eleven newly covered fields. This authorizes the report-specific policy; configuration schema, general precedence, other commands and #1892 diagnostic improvements remain excluded under the previously approved scope.

    Campaign 152593d7a535-fdfc969e-824d-4d69-a9ae-3ce95496903a resumes the same attempt and owner. No implementation, branch or PR exists yet. Next: freeze scope and validate assigned ADR 0338, then create the sibling worktree and design. Full candidate verification and independent design/scope/code/security review remain pending. No live HMC run or merge is authorized.

  5. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    interaction: interactive
    scope identity: #1891 — q1891-aaf6f53e
    outcome: Make report utilization's environment-override classification exhaustive without changing general configuration loading; deliver a green mergeable PR and authored MERGE-READY handoff, without merging.
    completion criteria: C1 classify each of the 21 HMCConfig fields exactly once, with timeout and schema_version global and the other 19 per-profile/refused (operator policy decision); C2 fail a test when a model field lacks classification (#1891); C3 refuse exported per-profile settings before report survey, preserving existing case-insensitive and empty-value refusal (#1891 and existing report contract); C4 document the full report-specific partition and retain both existing global allowances (#1891/operator); C5 focused tests and just verify, pinned prek hooks and CI green; no live HMC (#1891/repository workflow).
    provenance: Issue #1891 expected behavior and verification boundaries; existing report.py refusal and docs/cli.md report contract. Campaign identity 152593d7a535-fdfc969e-824d-4d69-a9ae-3ce95496903a dispatch authorizes quest through handoff, not merge. Human 2026-11-04 “Approve all displayed scopes” approved the unchanged exclusion/owner set. Human “Approve 19 refused / 2 global” explicitly approved the exact 21-field partition in issuecomment-6646053282, recorded/read back at #1891 (comment). General configuration precedence remains protected; report.py continues owning report-only admission policy, with no caller migration or compatibility facade needed.
    exclusions: General profile precedence, configuration schema and other commands — outside issue, operator owns; global-option diagnostic improvements — #1892 owns, serial after #1891 merge.
    surface: src/hmcpctl/cli_commands/report.py, tests/app/test_report_cli.py, docs/cli.md; design specification docs/workflow/specs/2026-11-04-report-config-classification-design.md and ignored local implementation plan docs/workflow/plans/2026-11-04-report-config-classification.md; exact assigned optional ADR docs/adr/0338-report-config-override-classification.md=0338. Read-only direct dependencies config.HMCConfig/model_fields/env_var_value/load_profile, current_options, report survey/client construction and test fixtures. Keep classification beside report refusal; remove no protected configuration paths and add no facade exports.
    ambiguities: none; operator settled all 21 fields. timeout/schema_version are the only globals; host, port, user, password, ssh_key_file, ssh_verify_host_key, verify_ssl, ca_bundle, cert_fingerprint, trust_system_store, upload_timeout, max_response_bytes, ssh_timeout, audit_memento, agent_id, authorize_power_operations, iso_url_allowlist, iso_build_entry and iso_build_arguments are per-profile/refused.

    Tracking metadata: live issue/source assessment; blast radius report.py, report CLI tests, cli.md and design records; hazards environment/configuration, credential trust, public override behavior and drift; complexity S; decompose one PR; classification non-trivial; artifact lane full-spec; routed review depth iterating.
    Design denominator: 100 changed lines (S) — live #1891 expected behavior, 21-field source inventory and bounded report admission check, revalidated against main c07423d. The source change stays in one report admission component; test/doc coverage expands with the explicit partition.
    Visible reservation scan: complete base/live-claim scopes/open PRs/pushed heads snapshot; assigned ADR 0338 has no foreign collision. No numbering reassignment.

  6. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    outcome: handed off — PR #1939 green+mergeable, awaiting human merge.

    The approved report-only classification is complete: 19 per-profile fields refused, timeout and schema_version global. Full local just verify passed once in 1232.120 seconds; separately required pinned prek passed in 121.188 seconds. Focused and assembled tests passed (120 and 403), with drift, refusal and global-allowance faults confirmed. CI run 41949013913 passed across amd64 Python 3.11–3.14, including downstream wheel checks; python-support-drift was the expected skipped job. No live HMC was used.

    One design review and one branch review completed across one charter; the scope audit and security pass approved. The design proportionality note and two accepted model suppressions are disclosed in the PR body and verified review comment. There are no new deferrals, follow-ups or bounty occurrences. Existing #1892 remains separate diagnostic work after this issue merges. No merge or worktree cleanup was performed.

    MERGE-READY: #1939 @ 83b080c

  7. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    Derivative handoff for PR #1939 after a bounded base refresh.
    Original author-approved head: 83b080c
    Current refreshed head: eb7bce3
    Exact-head checks and the captured base passed.

    MERGE-READY: #1939 @ eb7bce3

  8. randomparity commented on Nov 5, 2026

    @randomparity
    OwnerAuthor

    Campaign merge completed — issue #1891

    PR #1939 merged at 71b8a49 after exact-head CI and guarded merge checks for eb7bce3. One bounded base refresh preserved the operator-approved 19 refused / 2 global report policy and ADR0338. Full local verification and pinned hooks passed on the refreshed head. Issue closure independently verified. Existing #1892 remains separate diagnostics work. Post-merge base CI remains unverified. No new follow-ups or occurrences.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeffort:SSmall effort: a few hourspriority:P3Low priority: nice to have

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions