diff --git a/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F41-source-surface-leakage-codegen-primitive.md b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F41-source-surface-leakage-codegen-primitive.md new file mode 100644 index 0000000..bb8f186 --- /dev/null +++ b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F41-source-surface-leakage-codegen-primitive.md @@ -0,0 +1,136 @@ +--- +catalogue_id: F41 +title: "Source-surface leakage of codegen-internal primitive — type-suffix name fossilizes in user-facing API" +family: F1-Sediment (design-surface contamination sub-form) +severity: P1 +status: ratified_2026-05-20 +empirical_project: Cobrust Phase G sprint (2026-05-19/20) +cobrust_local_id: F38 (f38-source-surface-leakage-codegen-primitive.md) +date_ratified: 2026-05-20 +cobrust_sha: 46c0946 +resolution_adr: ADR-0064 (print-monomorphization-source-surface-cleanup) +constitutional_binding: CLAUDE.md §2.5 (LLM-first design principle, training-data-overlap rule) +--- + +# F41 — Source-surface leakage of codegen-internal primitive + +## Pattern + +A codegen-internal primitive — named by type shape (`_`, e.g., +`print_int`, `print_str`) — leaks into the source-face PRELUDE during a +demo sprint. It fossilizes when subsequent waves do not audit the question: +"is this name source-face API or codegen-internal symbol?" + +The leak path: + +1. Demo sprint needs to prove codegen works → quickest route is direct + monomorphic names (`print_int`, `print_str`). +2. Demo lands, wave closes, no cleanup ADR authored. +3. Next wave sees the names in PRELUDE, writes examples against them, + accumulates usage at call sites. +4. By the time an audit catches it, migration cost is non-trivial + (50-100+ call sites across examples, fixtures, skills). + +This is not a logic bug. It is a **design-surface contamination bug**: the +internal implementation vocabulary bleeds into the user vocabulary. + +## Root cause + +Two independent dynamics compound: + +- **Sprint-tempo bias**: demo-ware ships the shortest path to visible output. + Monomorphic names (`print_int`) are that shortest path. No gate asks "is + this user-facing?" at demo time. +- **Accumulation drift (F1 Sediment)**: wave-2 onward does not re-examine + whether PRELUDE entries are source-face intentional. Each usage is another + call site, each call site raises the migration cost, which raises the + perceived risk of cleanup, which delays the cleanup further. + +## Why this is critical for ADSD / LLM-first projects + +Per CLAUDE.md §2.5 (LLM-first design principle, constitutional north star): + +> Cobrust is the language LLM agents write correctly on the first try. + +The **training-data-overlap rule** is the key binding: + +- LLMs trained on Python/Rust write `print(x)` — one of the highest-frequency + call patterns in any Python corpus. +- `print_int(x)` appears in neither Python nor Rust training data. It is a + Cobrust-internal artifact. +- Result: LLM generates `print(x)` → `NameError: print_int is not defined` → + LLM confused by gap between prior and actual API → corrective loop consumes + tokens and latency for zero semantic value. + +Every type-suffix source-face name is a **friction multiplier on every future +LLM-driven generation session** against the codebase. + +## Empirical evidence (Cobrust 2026-05-19/20) + +**Affected names (Phase E demo era, Cobrust 2026-04):** + +| Source-face name (wrong) | Should be | Internal C-ABI symbol | +|--------------------------|--------------|--------------------------| +| `print_int` | `print` | `__cobrust_print_int` | +| `print_str` | `print` | `__cobrust_print_str` | +| `print_bool` | `print` | `__cobrust_print_bool` | +| `print_float` | `print` | `__cobrust_print_float` | + +**Call-site count at cleanup (ADR-0064 sprint):** +- 133 `.cb` call sites + ~200 Rust inline-source test strings refactored. +- Net source delta ~333 LOC across 4 cleanup commits. + +**Sprint commit references (Cobrust main):** +- `c73be4e` — PRELUDE table: remove `print_int`/`str`/`bool`/`float` source-face entries +- `b51b907` — polymorphic `print()` dispatch in `synth_call` + codegen monomorphization +- `5e87e77` — mechanical refactor: 133 `.cb` call sites + Rust inline strings → `print()` +- `46c0946` — Phase 4 fix: `Ty::None` callret locals must dispatch to `__cobrust_println_int` + not str-buf (caught by regression during cleanup) + +**Ratified at:** commit `46c0946` (feature/0064-print-mono, rebased on main 2026-05-20). + +**Post-ratification state:** +- Zero `print_int`/`print_str`/`print_bool`/`print_float` call-sites in any `.cb` file + under `examples/`. Confirmed via `grep -rEn "print_(int|str|bool|float)\(" examples/ --include="*.cb"` → empty. +- LC-100 12/12 maintained (including LC-05 which caught a `Ty::None` dispatch bug exposed by cleanup). +- 5+ integration tests passing for polymorphic `print`. + +## Detection rule (CI gate candidate) + +For every function listed in the PRELUDE source-face table: + +> If the function name matches `_` where `` ∈ +> `{int, str, bool, float, list, dict, set, tuple, ...}`, file an audit issue: +> "should this be polymorphic in source?" + +``` +for name in PRELUDE.source_face_names: + if re.match(r'^[a-z_]+_(int|str|bool|float|list|dict|set|tuple)$', name): + emit_audit_warning( + f"PRELUDE name '{name}' matches type-suffix pattern — " + "verify it is source-face intentional, not codegen-internal leakage" + ) +``` + +Candidate for a lint pass in CI. Zero false-positive risk on a well-curated PRELUDE: +intentional type-suffix names are rare; any hit deserves a justification comment. + +## Resolution path + +1. **Identify**: grep PRELUDE source-face table for `_` names. +2. **Classify**: for each hit, determine whether it is source-face intentional + (user writes it) or codegen-internal (should be hidden behind a polymorphic + dispatch). +3. **Cleanup sprint**: remove the monomorphic names from PRELUDE; add polymorphic + dispatch that routes `print(x: T)` to `__cobrust_print_T` post-typecheck. +4. **Mechanical refactor**: batch-rename all call sites (mirrors LC-100 &borrow + 226-site batch pattern — treat as a mechanical sprint, not a semantic one). +5. **Gate**: add CI lint to prevent re-introduction. + +## Related findings + +| Finding | Relationship | +|---------|--------------| +| F36 — fixture-name-vs-behavior drift (Cobrust F36) | Same family: wave-1 demo-ware fossilizes without audit checkpoint | +| F37 — silent-rot-on-accepted-debt (Cobrust F37) | Same family: accepted debt silently accumulates usage; no discipline at debt boundary | +| F1 — Declared rules without enforcement | Parent family: "design surface should be polymorphic" is common sense; no enforcement gate exists at PRELUDE authorship time | diff --git a/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F42-device-name-leakage-public-artifacts.md b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F42-device-name-leakage-public-artifacts.md new file mode 100644 index 0000000..b111c4c --- /dev/null +++ b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F42-device-name-leakage-public-artifacts.md @@ -0,0 +1,141 @@ +--- +catalogue_id: F42 +title: "Device-identifying names leaked into git history and repo files via sub-agent memory read-through" +family: F1-Sediment (opsec-boundary sub-form) +severity: P1 (privacy / opsec — identifying info in public repo) +status: ratified_2026-05-19 +empirical_project: Cobrust pre-publish privacy sweep (2026-05-19) +cobrust_local_id: F39 (f39-device-name-leakage-in-commits.md) +date_ratified: 2026-05-19 +cobrust_sha: d012df9 +resolution: git filter-repo force-rewrite + rename + CI grep gate (Option A) +discovered_by: P10 CTO emergency audit — pre-publish privacy sweep +--- + +# F42 — Device-identifying names leaked into public artifacts via sub-agent memory read-through + +## Pattern + +Sub-agents writing commit messages, ADRs, and module documentation frequently embed +**device-identifying strings** sourced from operator memory references — hostnames, +IP addresses, SSH port numbers, GPU model SKUs, OS kernel versions, user login names — +into public-repo artifacts that land on `main`. Pre-publish, this leaks operator +infrastructure opsec into a soon-public repository. + +The mechanism is a **memory read-through without opsec boundary**: + +1. Operator stores concrete connection info in agent memory (e.g., `reference_x86_workstation.md`) + so they can reconnect quickly between sessions. +2. Sub-agents reading that memory treat the literals as **publishable grounding detail** + (it "contextualizes" the work) rather than **opsec-sensitive material**. +3. No pre-write rule prohibits embedding these strings. CI does not grep commit/diff text + for banned patterns. +4. Strings accumulate in commit messages (not trivially rewriteable in a normal git flow), + ADRs, workflow files, and architecture pages over many sprint sessions. + +## Root cause + +This is F1-family: the rule "don't embed infrastructure literals in publishable text" +exists as common sense, but no enforcement gate verifies it at write time or CI time. + +Two independent contributing factors: + +- **Memory-to-artifact boundary ambiguity**: agents correctly use memory to orient + themselves. The distinction "this literal is ops-private" vs. "this literal is + publishable" is not enforced at the tool boundary. Any memory read can silently + propagate private literals into any subsequent write. +- **Commit message irreversibility**: file contents can be edited in place; commit + messages require history rewrite. The longer the leak persists, the more invasive + the remediation (force-push, filter-repo, coordinated branch cleanup). + +## Empirical evidence (Cobrust 2026-05-19, pre-rewrite) + +**Quantified leak inventory:** +- **31 commit messages** across `main` + feature branches contained one or more of: + `DG-Workstation-2x3090`, `wubingjing`, `112.74.60.44`, `port 10040`, `Linux 6.x kernel`. +- **18 repo files** carried the same strings inline: + - 8 ADRs + - 2 architecture pages + - 4 test files + - 1 module documentation page + - 1 spike document + - 1 GitHub Actions workflow file +- **Workflow filename** `.github/workflows/workstation-gates.yml` itself hinted at + the host identity tier via its name. + +**Remediation executed (Cobrust 2026-05-19):** +- `git filter-repo --replace-text` + `--replace-message` rewrote all branches, + mapping device-identifying strings to neutral placeholders: + - hostname → `` + - user login → `` + - IP address → `` + - SSH port → `` + - GPU model SKU → `` + - OS kernel version → `linux x86_64 host` +- 18 leftover worktree branches deleted (local + remote). +- Workflow renamed to `.github/workflows/self-hosted-gates.yml`. +- Force-pushed `main` with rewritten history (solo dev, no external consumers, + operator explicit authorization). +- Ratified at commit `d012df9`. + +## Detection rule (CI gate — open as of ratification) + +Add a pre-commit / CI grep gate that fails the build if any banned literal reappears: + +```bash +# .github/workflows/opsec-lint.yml (or pre-commit hook) +BANNED_PATTERNS=( + "DG-Workstation" # specific host class name + "wubingjing" # specific user login + "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" # any IPv4 (catch-all) + "port [0-9]{4,5}" # explicit SSH port references + "RTX [0-9]{4}" # GPU model SKU + "Linux [0-9]+\.[0-9]+" # minor kernel version +) +for pattern in "${BANNED_PATTERNS[@]}"; do + if git diff --cached | grep -qE "$pattern"; then + echo "OPSEC LINT FAIL: banned pattern '$pattern' in staged diff" + exit 1 + fi +done +``` + +Apply to commit messages via `commit-msg` hook as well as file content via `pre-commit`. + +## Going-forward rule + +When writing commit messages, ADRs, module docs, or any other publishable artifact, +**never** embed: + +- Specific hostnames (use `` or `runner host`). +- Specific user logins (use `` or `the operator account`). +- IP addresses (use `` or `the runner endpoint`). +- SSH port numbers (use `` or `the SSH port`). +- GPU model SKUs as tier identifiers (use `` or describe capability: "x86_64 GPU host with CUDA"). +- OS minor version + kernel version (use `linux x86_64 host`). + +Initials-only references (e.g., "DG verify", "on DG") are acceptable when the +two-letter token does not uniquely identify a public-facing artifact. + +## Resolution path + +If the leak has already accumulated: + +1. **Audit**: `git log --all --oneline | xargs -I{} git show {} -- | grep -E ""` to + quantify the blast radius across all branches and files. +2. **Triage**: separate file-content leaks (patchable in place) from commit-message leaks + (require filter-repo rewrite). +3. **Rewrite**: `git filter-repo --replace-text replacements.txt --replace-message replacements.txt` + where `replacements.txt` maps each banned literal to its neutral placeholder. +4. **Branch cleanup**: delete worktree branches that carried unrewritten history. +5. **Gate**: add CI opsec lint as described in the Detection Rule above. +6. **Memory cross-link**: add an in-repo finding file so future agents resuming without the + operator's memory entry still have the rule available. + +## Related findings + +| Finding | Relationship | +|---------|--------------| +| F1 — Declared rules without enforcement | Parent family: opsec boundary exists as common sense, but no enforcement gate at write time | +| F43 — SPOF heavy-build host (Cobrust F40) | Same origin: over-reliance on a named private host created both the opsec exposure (F42) and the availability failure (F43) | +| F35 — commit-message scope drift (upstream catalogue) | Adjacent: commit messages carry unintended context; this finding is the opsec variant | diff --git a/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F43-spof-heavy-build-host.md b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F43-spof-heavy-build-host.md new file mode 100644 index 0000000..4e92640 --- /dev/null +++ b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/F43-spof-heavy-build-host.md @@ -0,0 +1,131 @@ +--- +catalogue_id: F43 +title: "Single-point-of-failure heavy-build host — SSH-gated workstation as sole verification path collapses when host dies" +family: infrastructure-resilience (SPOF sub-form) +severity: P1 (pipeline halt — full sprint blocked on single host availability) +status: ratified_2026-05-20 +empirical_project: Cobrust Phase J wave-2 sprint (2026-05-19/20) +cobrust_local_id: F40 (f40-single-point-of-failure-heavy-build-host.md) +date_ratified: 2026-05-20 +cobrust_sha: 9cb84b5 +resolution: DG abandonment policy — all heavy gates route to GH Actions CI +--- + +# F43 — Single-point-of-failure heavy-build host + +## Pattern + +Depending on a single SSH-reachable workstation for full-workspace cargo verification +creates a single point of failure. When the host becomes unavailable — network reset, +ISP interruption, OS issue, power event — the entire heavy-build pipeline collapses +with no fallback path and no clear error escalation. + +The failure mode has three compounding layers: + +1. **Hard dependency**: all heavy-build gates (`cargo test --workspace`, + `cargo build --workspace`) route exclusively through the SSH host. +2. **Silent retry loop**: sub-agents follow their SOP and retry the SSH connection + on failure, consuming tool budget on failed invocations for the duration of the + outage, without escalating "host is unreachable — route to CI." +3. **No fallback policy**: no written rule exists for "if the SSH host is down, do + this instead." The agent cannot route around the failure. + +## Root cause + +This is an **infrastructure resilience gap** compounded by an **F1 Sediment pattern**: + +- The policy "heavy builds run on the SSH host" was written once into the dispatch + SOP (Mode C VERIFY LOOP). It was never given a "what if the host is down?" fallback. +- Sub-agents execute the SOP faithfully, including the retry loop, without the + meta-rule "if retry > N, escalate and route differently." +- The same implicit coupling that created the F42 opsec leak (over-reliance on a + named private host) also created this availability failure. + +## Why reproducibility matters for ADSD + +Per CLAUDE.md §3 dispatch reproducibility, verification must be reproducible by any +contributor. An SSH-credential-gated single host violates this in three ways: + +1. **Credential dependency**: new contributor (human or agent) cannot run heavy-build + gates without SSH credentials to the specific host. +2. **Availability dependency**: the host must be alive, reachable, and fully configured + (current repo clone, correct Rust toolchain, working PATH). +3. **Opacity**: any of the three failing silently stalls a sprint without a clear + diagnostic message distinguishing "code is broken" from "host is down." + +Any of these three failing silently is indistinguishable from a code regression +until diagnosed — wasting agent time on root-cause analysis of an infra issue. + +## Empirical evidence (Cobrust 2026-05-19/20) + +**Incident:** +- SSH endpoint failed throughout an 8+ hour session with: + `kex_exchange_identification: read: Connection reset by peer` +- Sub-agents continued retrying (per Mode C SOP) rather than escalating. +- Tool budget consumed on failed SSH invocations. +- Mac single-crate per-crate verify (`cargo test -p `) was sufficient to + unblock the session but was ad-hoc — no policy existed for this fallback. +- Host degradation went unflagged for the full session. + +**Archaeology SHA:** `9cb84b5` — the commit where the DG self-hosted-runner +abandonment policy was explicitly documented ("Mac single-crate + CI authoritative"). +Related: `d012df9` renamed `workstation-gates.yml` → `self-hosted-gates.yml` in the +same cleanup session. + +**Quantified cost:** 8+ hours of sprint time during which no heavy full-workspace +verification was possible; multiple sub-agent dispatches consumed tool budget on +failed SSH retries before escalation. + +## Resolution path (adopted 2026-05-20, Cobrust) + +**Adopted policy — DG abandonment / GH Actions primary:** + +- ALL heavy full-workspace cargo (`cargo test --workspace`, `cargo build --workspace`) + routes to GH Actions CI (ubuntu-latest + macos-latest matrix). +- Mac local = single-crate quick-feedback only (`cargo test -p `). +- No SSH credentials in dispatch templates. +- No `ssh -p @` patterns in SOP blocks. + +GH Actions is the authoritative 2-OS matrix verifier. It is reproducible, +credential-free, and available to all contributors. + +**Dispatch template change:** replace Mode C VERIFY LOOP SSH block with +"push branch → GH Actions CI passes → merge." + +## Detection rule (process audit) + +Signs that a project has drifted into SPOF-build territory: + +1. Any SOP template contains a literal `ssh -p @` command as a + required verification step. +2. No documented fallback for "what if that host is unreachable." +3. CI definition (`.github/workflows/`) references a `self-hosted` runner with no + redundancy (single runner label, no runner pool). +4. Sub-agents report "SSH connection refused / reset" but continue retrying rather + than escalating within 2-3 attempts. + +Remediation audit question: "Can a brand-new contributor with only a GitHub account +run every required verification gate?" If no, identify and route around the gap. + +## General ADSD mitigation + +For any ADSD project that uses a self-hosted runner or SSH-gated build host: + +1. **Define the fallback policy in writing** before the first sprint that uses the host. + Include: escalation threshold (e.g., "3 consecutive SSH failures"), fallback path + (e.g., "push to GH Actions CI"), and clear ownership. +2. **Use cloud CI as the authoritative gate**. Self-hosted runners are opt-in acceleration, + never the sole verification path. +3. **No host-specific identifiers in dispatch templates**. SOPs must be portable: any + runner with the right toolchain should satisfy the gate. +4. **Sub-agent retry cap**: dispatch prompts should include "if this SSH command fails + N consecutive times, stop retrying and report 'verification offloaded to CI'." + +## Related findings + +| Finding | Relationship | +|---------|--------------| +| F42 — Device-name leakage (Cobrust F39) | Co-origin: the same named private host created both the opsec exposure (F42) and the availability failure (F43) | +| F37 (Cobrust) — Silent-rot-on-accepted-debt | The host degradation was not escalated; sub-agents silently retried | +| F29 — Cross-platform runner-pool dependency (upstream catalogue) | Adjacent: F29 covers runner-pool failure at the CI-matrix level; F43 covers SSH-gated single host at the sprint-verification level | +| F1 — Declared rules without enforcement | Parent family: "have a fallback" is common sense; no enforcement gate at SOP authorship time | diff --git a/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/README.md b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/README.md new file mode 100644 index 0000000..56fa6e4 --- /dev/null +++ b/plugins/adsd/skills/agent-driven-development/reference/cobrust-f41-f43/README.md @@ -0,0 +1,69 @@ +--- +batch_id: cobrust-f41-f43 +title: "F41-F43: Cobrust empirical corroboration batch (source-surface leakage + device-name redaction + SPOF build host)" +date: 2026-05-21 +cobrust_baseline: Phase J wave-2 FULL CLOSED (main HEAD 53b5ed2 at time of filing) +prior_batch: cobrust-f31-f39 (PR #1, open) +--- + +# Cobrust F41-F43 batch — README + +Three new failure-mode findings empirically corroborated by Cobrust Phase G/J +sprints (2026-05-19/20), submitted as a follow-up to PR #1 (F31-F40 batch). + +## Slot mapping + +| This batch | Cobrust local ID | Ratified SHA | Incident date | +|------------|-----------------|--------------|---------------| +| **F41** | F38 — source-surface leakage of codegen primitive | `46c0946` | 2026-05-19/20 | +| **F42** | F39 — device-name leakage in commits + repo files | `d012df9` | 2026-05-19 | +| **F43** | F40 — single-point-of-failure heavy-build host | `9cb84b5` | 2026-05-19/20 | + +## Finding summaries + +### F41 — Source-surface leakage of codegen-internal primitive + +A codegen-internal monomorphic name (`print_int`, `print_str`, ...) leaks into the +source-face PRELUDE during a demo sprint and fossilizes as examples accumulate usage +against it. This directly violates the LLM-first training-data-overlap rule: LLMs +trained on Python/Rust write `print(x)`, not `print_int(x)`. Cleanup required 333 LOC +across 4 commits. Resolution: polymorphic dispatch + CI lint on type-suffix PRELUDE names. + +**Key metric:** 133 `.cb` call sites + ~200 Rust inline strings refactored. +**Cobrust SHA:** `46c0946` (ADR-0064 ratified). + +### F42 — Device-identifying names leaked into git history via sub-agent memory read-through + +Sub-agents treating operator memory references (SSH host, IP, port, GPU SKU) as +publishable grounding detail embedded opsec-sensitive strings into 31 commit messages +and 18 repo files before a pre-publish audit caught them. Required `git filter-repo` +history rewrite + force-push. Resolution: going-forward opsec boundary rule + CI grep gate. + +**Key metric:** 31 commit messages + 18 repo files rewritten. +**Cobrust SHA:** `d012df9` (Option-A privacy rewrite). + +### F43 — Single-point-of-failure heavy-build host + +Routing all full-workspace cargo verification through a single SSH-gated workstation +created a pipeline-halting SPOF: when the host died, sub-agents retried silently for 8+ +hours consuming tool budget, with no fallback policy. Resolution: DG abandonment — all +heavy gates route to GH Actions CI; Mac local = single-crate quick-feedback only. + +**Key metric:** 8+ hour sprint blocked on unavailable SSH host. +**Cobrust SHA:** `d012df9` (DG abandonment, same session as F42 remediation). + +## Files in this batch + +``` +cobrust-f41-f43/ + README.md (this file) + F41-source-surface-leakage-codegen-primitive.md + F42-device-name-leakage-public-artifacts.md + F43-spof-heavy-build-host.md +``` + +## Relationship to PR #1 (cobrust-f31-f39) + +This batch is independent of PR #1. It can be merged before or after PR #1. +The F41-F43 slot numbers were chosen to be free given that PR #1 claims F31-F40 +(using upstream F38/F39/F40 for different patterns than the Cobrust local ones).