From 26dd9e10680192c0e78856eb2bdfab10282d9b1c Mon Sep 17 00:00:00 2001 From: zerone0x <39543393+zerone0x@users.noreply.github.com> Date: Sat, 5 Sep 2026 20:03:19 +0000 Subject: [PATCH 1/6] fix(deploy): ignore Python bytecode downstream --- .agentcortex/bin/deploy.sh | 4 ++++ tests/ci/test_deploy_tiering.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/.agentcortex/bin/deploy.sh b/.agentcortex/bin/deploy.sh index d1b681ce..8c69d99b 100644 --- a/.agentcortex/bin/deploy.sh +++ b/.agentcortex/bin/deploy.sh @@ -1093,6 +1093,10 @@ write_downstream_ignore_block() { *.acx-incoming *.acx-local +# Python bytecode produced by Agentic OS validation/tools +__pycache__/ +*.pyc + # Per-Operator Tool State (this project's .claude/settings.json declares # settings.local.json user-local; keep git agreeing with that declaration) .claude/settings.local.json diff --git a/tests/ci/test_deploy_tiering.py b/tests/ci/test_deploy_tiering.py index 0faac664..04b7f882 100644 --- a/tests/ci/test_deploy_tiering.py +++ b/tests/ci/test_deploy_tiering.py @@ -418,6 +418,18 @@ def test_deploy_gitignores_acx_local_sidecar() -> None: assert "*.acx-local" in s, "deploy.sh must add *.acx-local to the managed .gitignore block" +def test_deploy_gitignores_python_bytecode() -> None: + """#430: Agentic OS Python tools can create bytecode in downstream trees. + + The source repository ignores these files; the managed downstream + .gitignore block must do the same so running validate does not dirty an + adopter's first framework commit with interpreter-specific artifacts. + """ + s = DEPLOY_SH.read_text(encoding="utf-8") + assert "__pycache__/" in s, "deploy.sh must add __pycache__/ to the managed .gitignore block" + assert "*.pyc" in s, "deploy.sh must add *.pyc to the managed .gitignore block" + + @requires_powershell def test_deploy_ps1_entrypoint_resolves_real_bash() -> None: with tempfile.TemporaryDirectory() as td: From ceed4c56f25e0f1b2ccfb22222fb6dcce38ce890 Mon Sep 17 00:00:00 2001 From: KbWen Date: Mon, 14 Sep 2026 10:22:51 +0800 Subject: [PATCH 2/6] fix(deploy): scope bytecode ignore to the framework and keep re-deploy idempotent (#430) Completes #435. The submitted patterns were not in the managed[] strip table, so every re-deploy left the old block outside the markers and the adopter's .gitignore grew by 14 lines (measured 33 -> 47 -> 61). - Emit `.agentcortex/**/__pycache__/` instead of repo-wide `__pycache__/` and `*.pyc`: all deployed .py files live under .agentcortex/, and a repo-wide rule would also ignore the adopter's own bytecode. - Add the entry to managed[] so the block is replaced, not duplicated. - Tests: every emitted entry must be strippable; every deployed .py must sit under the scoped pattern; a real double deploy must leave .gitignore byte-identical, keep adopter rules, ignore framework bytecode and not the adopter's. - Backlog #191 -> In Progress. Co-authored-by: zerone0x <39543393+zerone0x@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) --- .agentcortex/bin/deploy.sh | 6 +-- docs/specs/_product-backlog.md | 2 +- tests/ci/test_deploy_tiering.py | 88 +++++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/.agentcortex/bin/deploy.sh b/.agentcortex/bin/deploy.sh index 8c69d99b..174028df 100644 --- a/.agentcortex/bin/deploy.sh +++ b/.agentcortex/bin/deploy.sh @@ -1093,9 +1093,8 @@ write_downstream_ignore_block() { *.acx-incoming *.acx-local -# Python bytecode produced by Agentic OS validation/tools -__pycache__/ -*.pyc +# Framework Tool Bytecode (framework namespace only; project policy untouched) +.agentcortex/**/__pycache__/ # Per-Operator Tool State (this project's .claude/settings.json declares # settings.local.json user-local; keep git agreeing with that declaration) @@ -1129,6 +1128,7 @@ strip_managed_ignore_blocks() { managed[".agentcortex-src/"] = 1 managed["*.acx-incoming"] = 1 managed["*.acx-local"] = 1 + managed[".agentcortex/**/__pycache__/"] = 1 managed[".claude/settings.local.json"] = 1 managed[".openrouter/"] = 1 managed[".claude-chat/"] = 1 diff --git a/docs/specs/_product-backlog.md b/docs/specs/_product-backlog.md index 5340f1d4..0364ea74 100644 --- a/docs/specs/_product-backlog.md +++ b/docs/specs/_product-backlog.md @@ -156,7 +156,7 @@ Governance file bloat review (2026-04-12) identified 10 findings across P0–P2: | 188 | Brownfield FIRST install silently destroys pre-existing files on all 142 core-tier paths - verified 2026-09-05. `deploy.sh` guards the *update* branch (locally-modified files are sidecar-preserved to `.acx-incoming`) and correctly `[SKIP]`s the three platform entry points, but on a first install into a project that already has a file at a core-tier path it overwrites in place: no `.acx-incoming`, no backup, **zero mention in the deploy output**, exit 0. Reproduced with an untracked `.agent/rules/engineering_guardrails.md` - sentinel gone, `git log` on the path returns 0 commits, so the content is unrecoverable; a tracked file survives only via `git diff`, which the banner never tells the adopter to check. `grep -c '^core ' .agentcortex-manifest` = 142 exposed paths, including all of `.claude/commands/*.md` and `.agent/rules/*`. Highest-blast-radius downstream defect in this batch. ADR-005 documents skills-to-sidecar and framework-authoritative-to-force-update but has **no brownfield-first-install case**, so the fix amends the ADR's preservation contract rather than patching one branch - needs a spec, not a quick patch. Related: #193 (same scenario, banner-honesty half). | framework | deploy | P1 | - | feature | Pending | - | - | | 189 | The Active Backlog's advertised read cost is understated ~170x - measured 2026-09-05. `AGENTS.md:36` tells every agent `_product-backlog.md` is a `Free-read (~200 tokens)`; the file is 139,206 chars = **~34,800 tokens**. The documented mitigation does not mitigate: `bootstrap.md:171` says to read *only* the Feature Inventory table, and that table is 137,998 chars - **99.1% of the file**. The same `~200 tokens` figure is repeated at `spec-intake.md:64`, `:349` and `:362`, so four call sites are wrong together. Consequence: an agent following `AGENTS.md` at bootstrap believes it is spending 200 tokens and spends ~35k - on the one growth surface `check_ssot_caps.py` does **not** cover (it caps only Ship History, currently 10/10, and Spec Index, 27/30, both inside `current_state.md`). Fix is two-part and the second half is the real work: correct the four figures (trivial), then decide whether the backlog gets a cap/rotation like the SSoT sections - which overlaps #1/#3/#11 and must not be re-solved independently. | docs | governance | P2 | - | quick-win | Pending | - | #1 | | 190 | Both validator twins ignore `.agent/config.yaml` `downstream_capabilities.path` and hardcode the default - verified 2026-09-05. `config.yaml:112` publishes `path:` as an adopter-settable key and `bootstrap.md:116` explicitly instructs the agent to check that key before loading; but `validate.sh:647` (`CAP_FILE=`) and `validate.ps1:791` (`$capFile`) both pin `.agentcortex/context/private/downstream-capabilities.yaml` literally. An adopter who retargets `path:` therefore gets the documented behaviour from the AI (it reads the retargeted file) and a silent green from the gate-safety validator (it inspects a path that no longer exists and reports SKIP/PASS). That is the ADR-007 invariant - gate-relaxation must be *unrepresentable* - being bypassable via a supported config edit. Scope note: the caps themselves (`load_policy_ceiling`, `custom_prefix_required`) are enforced correctly by `validate_downstream_capabilities.py`; only the **path resolution** is unwired, so the fix is small and must land in both twins together (cross-platform parity). | framework | governance | P1 | - | quick-win | Pending | - | - | -| 191 | The deployed `.gitignore` block omits `__pycache__/` and `*.pyc`, so the framework's own tools dirty the adopter's tree - verified 2026-09-05. The source repo protects itself at `.gitignore:50-51`; the block `deploy.sh` writes downstream does not carry those two lines. Running the framework's own documented self-check (`bash .agentcortex/bin/validate.sh`) in a fresh target immediately creates `.agentcortex/tools/__pycache__/*.pyc`, and the deploy banner's own "Finish setup" line (`git add .agentcortex-manifest AGENTS.md ... .agentcortex/ ...`) stages them. An adopter who follows the printed instructions in the printed order commits Python bytecode on their first framework commit. Smallest correct fix is additive and unambiguous: add the two patterns to the downstream ignore-defaults block in `deploy.sh` (and the PowerShell path), then extend the deploy-tiering test that pins the ignore block. Filed separately from #188/#193 on purpose - it is the one finding in this batch with no design question attached. | framework | deploy | P2 | - | quick-win | Pending | [#430](https://github.com/KbWen/agentic-os/issues/430) | - | +| 191 | The deployed `.gitignore` block omits `__pycache__/` and `*.pyc`, so the framework's own tools dirty the adopter's tree - verified 2026-09-05. The source repo protects itself at `.gitignore:50-51`; the block `deploy.sh` writes downstream does not carry those two lines. Running the framework's own documented self-check (`bash .agentcortex/bin/validate.sh`) in a fresh target immediately creates `.agentcortex/tools/__pycache__/*.pyc`, and the deploy banner's own "Finish setup" line (`git add .agentcortex-manifest AGENTS.md ... .agentcortex/ ...`) stages them. An adopter who follows the printed instructions in the printed order commits Python bytecode on their first framework commit. Smallest correct fix is additive and unambiguous: add the two patterns to the downstream ignore-defaults block in `deploy.sh` (and the PowerShell path), then extend the deploy-tiering test that pins the ignore block. Filed separately from #188/#193 on purpose - it is the one finding in this batch with no design question attached. | framework | deploy | P2 | - | quick-win | In Progress | [#430](https://github.com/KbWen/agentic-os/issues/430) | - | | 192 | `.agent/rules/repo-gotchas.md` ships to adopters citing this repo's own undeployed paths - verified 2026-09-05. It is a `core` manifest entry and is routed to from the `AGENTS.md` Repo Gotchas reference line, so a downstream agent is told to consult it before touching those surfaces. Four **full-path** citations do not exist in a deployed tree: `.agentcortex/tests/test_lifecycle_token_consumption.py`, `tests/ci/test_deploy_tiering.py`, `tests/ci/test_pre_commit_hook.py`, `tests/ci/test_subprocess_encoding.py` (`tests/` is not in the deploy set). Bare-basename citations in the same file were **excluded** from this count - they are the exact false-positive class #185 describes, and quoting a combined total would reproduce the very defect #185 is about. The file also carries this repo's PR numbers and its own release ritual, which are meaningless downstream. Resolve by settling scope first: either stop shipping it (DELETE - it is arguably an upstream-maintainer doc), or split the genuinely portable traps from the repo-local ones. Do not simply fix the links. | docs | adoption | P3 | - | quick-win | Pending | - | #185 | | 193 | A brownfield install ends validator-RED while the deploy banner reports success - verified 2026-09-05. In the same target as #188, `bash .agentcortex/bin/validate.sh` exits 1 with `pass=83 warn=1 fail=3 skip=8`: `[FAIL] safety nucleus freshness`, `[FAIL] work log contract references are stale`, and `[FAIL] AGENTS.md missing routing index reference (authority handoff absent)`. The third is load-bearing - `deploy.sh` correctly `[SKIP]`s the adopter's own `AGENTS.md`/`CLAUDE.md`, and the very same run then prints `Platform Entry Points Ready: AGENTS.md / CLAUDE.md / GEMINI.md`. The adopter is told the entry points are wired at the moment the validator's own verdict is that the authority handoff is absent: the framework is on disk with zero governance reach, and nothing in the deploy output says so. This is the **honesty half** of #188 and has a much smaller fix than the preservation half - the banner should report what the run actually did (skipped vs installed) and point at the merge step, or the deploy should end by running the self-check instead of asserting readiness. Same axis as the downstream-signal-honesty work already shipped in `dev-flow-hardening`. | framework | adoption | P2 | - | quick-win | Pending | - | #188 | | 194 | **Downstream-audit coverage gap — 8 of 16 planned scenarios never ran (2026-09-05).** The pass that produced #188-#193 was halted early to conserve budget, so these surfaces are **unaudited, not clean**, and no later row should be read as clearing them: (a) *zero-Python* — whether a check that could not run reports as PASS, vanishes from the summary, or leaves the exit code green; also whether `credential_floor.sh`/`.ps1` actually executes with Python absent. **Highest-value gap: this is the one surface with a plausible silent false-pass.** (b) *re-deploy/upgrade* — idempotency (a no-change second deploy must report SKIP, not "updated"), local-modification tiering, orphan removal, stale-manifest handling. (c) *Windows-native* — space-in-path targets, the `compute_sha256` leading-backslash hazard (a broken hash comparison shows up as everything being "updated" on an unchanged tree), CRLF on deployed `.sh`. (d) *non-git and nested-in-parent-repo targets* — whether validators walk up to the outer repo; worklog-key derivation on a detached HEAD or a branch name illegal as a Windows filename. (e) *greenfield* — virgin-install validator noise; manifest-vs-disk spot check. (f) *validator parity* — `validate.sh` (3105 lines) vs `validate.ps1` (2895) has never been diffed check-for-check; the delta is unexplained and may be missing coverage rather than shell verbosity. (g) *skill shipping* — whether `.agents/skills/*/SKILL.md` bodies actually reach a deployed tree (a stub layer without bodies would mean every skill silently resolves to nothing downstream). (h) *gate-bypass battery* — phase skipping, a `NOT READY` receipt satisfying ship, forged guard receipts, lock stealing, `INDEX.jsonl` middle-row edit and tail truncation. **Resume recipe — plain shell, no orchestration tooling required.** Each scenario is independent; deploy once into a scratch dir and work there. Setup: `mkdir -p /tmp/acx/t && cd /tmp/acx/t && git init -q . && echo x > f.txt && git add -A && git commit -qm i && bash /.agentcortex/bin/deploy.sh .` Then for (a): confirm Python is unreachable first with `env PATH=/usr/bin:/bin bash -lc 'command -v python python3 py'`, then run `env PATH=/usr/bin:/bin bash .agentcortex/bin/validate.sh` and diff its per-check output against a normal run - an honest visible `SKIP`/`WARN` that is counted in the summary is CORRECT; a PASS, a silent disappearance, or a green exit is the defect. For (b): deploy twice with no changes and read the counters. For (f): extract the check labels from each twin and diff the sets. **Do NOT try to resume the original run** - `resumeFromRunId` is same-session-only and that session is closed; the raw returns of the 8 scenarios that DID complete are in that run's `journal.jsonl` under `~/.claude/projects///subagents/workflows/wf_15af2b9b-3f2/` on the machine that ran it, and are not portable. **Also unfiled on purpose**: those 8 completed reports contained ~12 further findings the primary agent did not reproduce; they were withheld per the evidence-before-adding norm, not lost. Recurring themes worth re-deriving from scratch: the deploy banner's `git add` line omitting `.gitignore`/`.githooks/`/`.gitattributes` (hit by 3 independent agents); `security_guardrails.md` section 6's "ship gate = FAIL" labelled enforced with no mechanism located; the token ceiling being enforced by zero *required* status checks; `.agentcortex/docs/README.md` shipping as the repo-root README and dangling its intra-doc links downstream. Treat each as an unverified lead requiring its own reproduction before filing. Suggested order: (a) first, then #190 (smallest real fix), then (f). Related: #188-#193 (what the same pass did verify). | framework | governance | P2 | - | quick-win | Pending | - | #188, #190 | diff --git a/tests/ci/test_deploy_tiering.py b/tests/ci/test_deploy_tiering.py index 04b7f882..09bffb7b 100644 --- a/tests/ci/test_deploy_tiering.py +++ b/tests/ci/test_deploy_tiering.py @@ -15,6 +15,7 @@ import hashlib import os +import re import shutil import subprocess import tempfile @@ -418,16 +419,85 @@ def test_deploy_gitignores_acx_local_sidecar() -> None: assert "*.acx-local" in s, "deploy.sh must add *.acx-local to the managed .gitignore block" -def test_deploy_gitignores_python_bytecode() -> None: - """#430: Agentic OS Python tools can create bytecode in downstream trees. - - The source repository ignores these files; the managed downstream - .gitignore block must do the same so running validate does not dirty an - adopter's first framework commit with interpreter-specific artifacts. - """ +def _deploy_ignore_block_and_managed_table() -> tuple[list[str], set[str]]: + """Parse the two halves of deploy.sh's .gitignore handling separately: + the entries write_downstream_ignore_block() emits, and the managed[] + table strip_managed_ignore_blocks() removes on the next deploy.""" s = DEPLOY_SH.read_text(encoding="utf-8") - assert "__pycache__/" in s, "deploy.sh must add __pycache__/ to the managed .gitignore block" - assert "*.pyc" in s, "deploy.sh must add *.pyc to the managed .gitignore block" + heredoc = re.search(r"write_downstream_ignore_block\(\) \{\n\s*cat <<'EOT'\n(.*?)\nEOT\n", s, re.S) + assert heredoc, "could not locate the heredoc in write_downstream_ignore_block()" + entries = [ + line for line in heredoc.group(1).splitlines() + if line.strip() and not line.startswith("#") + ] + managed = set(re.findall(r'managed\["([^"]+)"\] = 1', s)) + assert entries and managed, "parsed an empty ignore block or managed[] table" + return entries, managed + + +def test_deploy_ignore_block_entries_are_all_strippable() -> None: + """PR #435 review: every entry deploy.sh writes into the managed .gitignore + block must also be in the managed[] strip table. A missing entry ends the + strip at that line on the next deploy, so the rest of the old block + survives outside the markers and the adopter's .gitignore grows every time.""" + entries, managed = _deploy_ignore_block_and_managed_table() + missing = [e for e in entries if e not in managed] + assert not missing, f"add these to managed[] in strip_managed_ignore_blocks(): {missing}" + + +def test_deploy_ignores_framework_bytecode_in_framework_namespace_only() -> None: + """#430: validate.sh runs the framework's Python tools, which write + __pycache__/ into the adopter's tree and get staged by the banner's own + `git add .agentcortex/`. The block ignores that bytecode inside the + framework namespace only, so it must cover every .py deploy ships.""" + entries, _ = _deploy_ignore_block_and_managed_table() + assert ".agentcortex/**/__pycache__/" in entries + for repo_wide in ("__pycache__/", "*.pyc"): + assert repo_wide not in entries, ( + f"{repo_wide} would also ignore the adopter's own files; keep the pattern framework-scoped" + ) + deployed_py = [ + parts[1] for parts in (line.split() for line in DEPLOY_MANIFEST_GOLDEN.read_text(encoding="utf-8").splitlines()) + if len(parts) >= 2 and parts[1].endswith(".py") + ] + assert deployed_py, "golden lists no deployed .py files; the coverage check would pass vacuously" + uncovered = [p for p in deployed_py if not p.startswith(".agentcortex/")] + assert not uncovered, f"deployed .py outside .agentcortex/ is not covered by the bytecode ignore: {uncovered}" + + +@requires_bash +@pytest.mark.skipif(git_path is None, reason="git not available") +def test_redeploy_leaves_gitignore_unchanged_and_adopter_policy_alone() -> None: + """#430 / PR #435: a second deploy must write a byte-identical .gitignore, + keep the adopter's own rules, ignore framework bytecode, and leave the + adopter's own bytecode to the adopter's own rules.""" + with tempfile.TemporaryDirectory() as td: + target = Path(td) / "proj" + target.mkdir() + subprocess.run([git_path, "init", "-q", str(target)], check=True) + adopter_rules = "node_modules/\ndist/\n" + gitignore = target / ".gitignore" + gitignore.write_text(adopter_rules, encoding="utf-8", newline="\n") + + assert _deploy(target).returncode == 0, "first deploy failed" + first = gitignore.read_bytes() + assert _deploy(target).returncode == 0, "second deploy failed" + assert gitignore.read_bytes() == first, "re-deploy changed .gitignore" + assert first.decode("utf-8").replace("\r\n", "\n").startswith(adopter_rules), \ + "adopter's own ignore rules must stay first and verbatim" + + framework_pyc = target / ".agentcortex" / "tools" / "__pycache__" / "guard_context_write.cpython-314.pyc" + adopter_pyc = target / "app" / "__pycache__" / "main.cpython-314.pyc" + for pyc in (framework_pyc, adopter_pyc): + pyc.parent.mkdir(parents=True, exist_ok=True) + pyc.write_bytes(b"") + + def ignored(path: Path) -> bool: + rel = path.relative_to(target).as_posix() + return subprocess.run([git_path, "-C", str(target), "check-ignore", "-q", rel]).returncode == 0 + + assert ignored(framework_pyc), "framework bytecode must be ignored" + assert not ignored(adopter_pyc), "the adopter's own bytecode must be left to the adopter's rules" @requires_powershell From 6a3de6ef2fbdbebf77b5b5f5397841eb3ba68889 Mon Sep 17 00:00:00 2001 From: KbWen Date: Mon, 14 Sep 2026 10:46:26 +0800 Subject: [PATCH 3/6] fix(deploy): write the bytecode entry last and isolate its test from host git config (#430) Review follow-ups on #435: - An older deploy.sh stops stripping at the first block entry it does not know, leaving every later block line outside the markers. With the new entry mid-block, alternating main/branch deploys grew .gitignore by 10-13 lines per round (measured 36 -> 46 -> 49 -> 59 -> 62 -> 72). Placed last, a downgrade leaves one stray line; alternation adds 3 lines per round (36 -> 36 -> 39 -> 39 -> 42). The managed[] table now says to add future entries last. - The double-deploy test ran `git check-ignore` under the developer's global excludes file, so a machine ignoring __pycache__/ globally failed it on correct code. It now passes an empty core.excludesFile. Co-authored-by: zerone0x <39543393+zerone0x@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) --- .agentcortex/bin/deploy.sh | 11 +++++++---- tests/ci/test_deploy_tiering.py | 10 +++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.agentcortex/bin/deploy.sh b/.agentcortex/bin/deploy.sh index 174028df..54d41f97 100644 --- a/.agentcortex/bin/deploy.sh +++ b/.agentcortex/bin/deploy.sh @@ -1093,9 +1093,6 @@ write_downstream_ignore_block() { *.acx-incoming *.acx-local -# Framework Tool Bytecode (framework namespace only; project policy untouched) -.agentcortex/**/__pycache__/ - # Per-Operator Tool State (this project's .claude/settings.json declares # settings.local.json user-local; keep git agreeing with that declaration) .claude/settings.local.json @@ -1106,6 +1103,9 @@ write_downstream_ignore_block() { .cursor/ .antigravity/scratch/ +# Framework Tool Bytecode (framework namespace only; project policy untouched) +.agentcortex/**/__pycache__/ + # End Agentic OS Template - Downstream Ignore Defaults EOT } @@ -1128,12 +1128,15 @@ strip_managed_ignore_blocks() { managed[".agentcortex-src/"] = 1 managed["*.acx-incoming"] = 1 managed["*.acx-local"] = 1 - managed[".agentcortex/**/__pycache__/"] = 1 managed[".claude/settings.local.json"] = 1 managed[".openrouter/"] = 1 managed[".claude-chat/"] = 1 managed[".cursor/"] = 1 managed[".antigravity/scratch/"] = 1 + # Add new entries here AND last in the block above: an older deploy.sh + # stops stripping at the first entry it does not know, so every block + # line after that entry would be left behind outside the markers. + managed[".agentcortex/**/__pycache__/"] = 1 # Legacy paths from older versions (strip during upgrade) managed["AGENTS.md"] = 1 managed["CLAUDE.md"] = 1 diff --git a/tests/ci/test_deploy_tiering.py b/tests/ci/test_deploy_tiering.py index 09bffb7b..8f701677 100644 --- a/tests/ci/test_deploy_tiering.py +++ b/tests/ci/test_deploy_tiering.py @@ -492,9 +492,17 @@ def test_redeploy_leaves_gitignore_unchanged_and_adopter_policy_alone() -> None: pyc.parent.mkdir(parents=True, exist_ok=True) pyc.write_bytes(b"") + # Judge only the target's .gitignore: a developer's global excludes file + # (commonly listing __pycache__/) would otherwise decide the result. + no_global_excludes = Path(td) / "empty-excludes" + no_global_excludes.write_text("", encoding="utf-8") + def ignored(path: Path) -> bool: rel = path.relative_to(target).as_posix() - return subprocess.run([git_path, "-C", str(target), "check-ignore", "-q", rel]).returncode == 0 + return subprocess.run([ + git_path, "-c", f"core.excludesFile={no_global_excludes.as_posix()}", + "-C", str(target), "check-ignore", "-q", rel, + ]).returncode == 0 assert ignored(framework_pyc), "framework bytecode must be ignored" assert not ignored(adopter_pyc), "the adopter's own bytecode must be left to the adopter's rules" From 52ef6386a1da8401e8773cbba53c46c45e2c1761 Mon Sep 17 00:00:00 2001 From: KbWen Date: Mon, 14 Sep 2026 12:11:07 +0800 Subject: [PATCH 4/6] test(deploy): keep the double-deploy test on the Python 3.9 floor (#430) `Path.write_text(newline=)` is 3.10+; test_write_text_newline_ratchet flagged it in the full suite. Write the adopter fixture as bytes. Co-Authored-By: Claude Opus 5 (1M context) --- tests/ci/test_deploy_tiering.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci/test_deploy_tiering.py b/tests/ci/test_deploy_tiering.py index 8f701677..5f134bf3 100644 --- a/tests/ci/test_deploy_tiering.py +++ b/tests/ci/test_deploy_tiering.py @@ -477,7 +477,7 @@ def test_redeploy_leaves_gitignore_unchanged_and_adopter_policy_alone() -> None: subprocess.run([git_path, "init", "-q", str(target)], check=True) adopter_rules = "node_modules/\ndist/\n" gitignore = target / ".gitignore" - gitignore.write_text(adopter_rules, encoding="utf-8", newline="\n") + gitignore.write_bytes(adopter_rules.encode("utf-8")) assert _deploy(target).returncode == 0, "first deploy failed" first = gitignore.read_bytes() From a45f4f6f07c1ec87fee36633a6123116e82cf851 Mon Sep 17 00:00:00 2001 From: KbWen Date: Mon, 14 Sep 2026 12:13:16 +0800 Subject: [PATCH 5/6] docs(ship): record the #435 takeover and ship backlog #191 SSoT Ship History entry (rotated at cap 10), heartbeat 170 -> 171, backlog #191 Shipped with the row rewritten to what actually shipped, Work Log archived and chained into INDEX.jsonl. Co-Authored-By: Claude Opus 5 (1M context) --- .agentcortex/context/.guard_receipt.json | 6 +- .agentcortex/context/archive/INDEX.jsonl | 1 + ...nstream-ignore-python-bytecode-20260914.md | 181 ++++++++++++++++++ .../context/archive/ship-history-2026.md | 11 ++ .agentcortex/context/current_state.md | 24 +-- docs/specs/_product-backlog.md | 4 +- 6 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 .agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md diff --git a/.agentcortex/context/.guard_receipt.json b/.agentcortex/context/.guard_receipt.json index 4ce1f88c..9cc3b9e6 100644 --- a/.agentcortex/context/.guard_receipt.json +++ b/.agentcortex/context/.guard_receipt.json @@ -1,7 +1,7 @@ { - "expected_sha": "eea13364dff3aceb2ea20b850256742f630f028b61d53983d61f1537590a0a38", + "expected_sha": "e911bdf70de6a546bb4ebe8ec81deb6635686b0b48b1a05530879c5d9b8ae458", "mode": "replace", - "new_sha": "e911bdf70de6a546bb4ebe8ec81deb6635686b0b48b1a05530879c5d9b8ae458", + "new_sha": "e2c9006167cbf4c4768d530ba9b7cfe647086093be1c6fcb4358c89ca7e054c6", "target": ".agentcortex/context/current_state.md", - "timestamp": 1789026065 + "timestamp": 1789359115 } diff --git a/.agentcortex/context/archive/INDEX.jsonl b/.agentcortex/context/archive/INDEX.jsonl index eb850730..b0f2b03f 100644 --- a/.agentcortex/context/archive/INDEX.jsonl +++ b/.agentcortex/context/archive/INDEX.jsonl @@ -177,3 +177,4 @@ {"branch": "docs/skill-description-clarity", "classification": "quick-win", "decisions": ["Reviewer log archived by MOVE only; its own receipts left untouched"], "log": "codex-final-review-docs-skill-description-clarity-20260910.md", "modules": ["docs/reviews/2026-09-09-cross-model-skill-final-review.md"], "patterns": ["independent-cross-vendor-review"], "prev_sha": "6ee47bc7", "shipped": "2026-09-10", "specs": ["docs/reviews/2026-09-09-cross-model-skill-handback.md"]} {"archive_file": ".agentcortex/context/archive/global-lessons-archive.md", "archived_at": "2026-09-10", "archived_body_sha": "7d331603", "archived_prev": "GENESIS", "prev_sha": "13dc602a", "successor_body_sha": "4faa557a", "successor_new_prev": "GENESIS", "type": "lesson_archive"} {"branch": "docs/retro-skill-description-lessons", "classification": "quick-win", "decisions": ["D-1: archive [classification-flow] to free a Global Lessons slot (user choice)", "D-2: add one lesson, not four"], "log": "docs-retro-skill-description-lessons-20260910.md", "modules": [".agentcortex/context/current_state.md", ".agent/rules/repo-gotchas.md", ".agentcortex/context/archive/ship-history-2026.md"], "patterns": ["retro", "memory-to-repo-records", "ship-history-rotation"], "prev_sha": "e9446cc6", "shipped": "2026-09-10", "specs": []} +{"branch": "fix/downstream-ignore-python-bytecode", "classification": "quick-win", "decisions": ["D-1: framework-scoped .agentcortex/**/__pycache__/ instead of repo-wide __pycache__/ + *.pyc", "D-2: new ignore-block entries go last; no marker-bounded strip"], "log": "fix-downstream-ignore-python-bytecode-20260914.md", "modules": [".agentcortex/bin/deploy.sh", "tests/ci/test_deploy_tiering.py"], "patterns": ["contributor-pr-takeover", "downstream-gitignore", "redeploy-idempotency"], "prev_sha": "cecfa10e", "shipped": "2026-09-14", "specs": []} diff --git a/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md b/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md new file mode 100644 index 00000000..7f8c5dd2 --- /dev/null +++ b/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md @@ -0,0 +1,181 @@ +# Work Log: fix/downstream-ignore-python-bytecode + +## Header + +- Branch: `fix/downstream-ignore-python-bytecode` +- Classification: `quick-win` +- Classified by: `claude-opus-5` +- Frozen: `2026-09-14` +- Created Date: `2026-09-14` +- Owner: `KbWen` +- Guardrails Mode: `Quick` +- Current Phase: `ship` +- Diff Base SHA: `3667fe6` +- Checkpoint SHA: `52ef638` +- Recommended Skills: `none` +- Primary Domain Snapshot: `deploy` +- SSoT Sequence: `170` + +--- + +## Session Info + +- Agent: `claude-opus-5` +- Session: `2026-09-14T02:04:28Z` +- Platform: `claude-code` + +--- + +## Task Description + +Take over contributor PR #435 (issue #430, backlog #191) after a week without response to the requested change, with the user's approval. The submitted fix leaves the deployed `.gitignore` growing by 14 lines on every re-deploy; finish it so the framework's bytecode is ignored without cluttering, or changing the ignore policy of, the adopter's own tree. + +--- + +## Phase Sequence + +| Phase | Status | Entered | Notes | +|---|---|---|---| +| bootstrap | done | 2026-09-14 | quick-win; SSoT read; contributor branch checked out, `origin/main` merged in (no force-push) | +| plan | done | 2026-09-14 | 2 product files + backlog row; see Phase Summary | +| implement | done | 2026-09-14 | `ceed4c5`; review fixes `6a3de6e` | +| review | done | 2026-09-14 | fresh same-vendor subagent, diff + issue + owner constraint only; 0 blockers, 2 should-fix (both fixed), 3 nits | +| test | done | 2026-09-14 | full CI-equivalent suite at `6a3de6e` (1 own failure, fixed `52ef638`) + changed-file rerun | +| handoff | skipped | - | quick-win exempt per AGENTS.md §Delivery Gates | +| ship | done | 2026-09-14 | SSoT Ship History (rotated at cap 10) + heartbeat 171, backlog #191 Shipped, log archived, INDEX chained | + +--- + +## Phase Summary + +- bootstrap: quick-win (2 modules: `deploy.sh` ignore block + its test; no gate/engine change). Reproduced the maintainer's review finding on a real deploy before planning, see Evidence. +- plan: (1) `deploy.sh`: replace the two repo-wide lines with one framework-scoped line `.agentcortex/**/__pycache__/`, and add it to the `managed[]` strip table. (2) `test_deploy_tiering.py`: replace the whole-file grep test with one that parses the heredoc block and the `managed[]` table separately and asserts every block entry is strippable, plus that every `.py` in the deploy golden lives under `.agentcortex/` so the scoped line covers it; add one behavioral test that deploys twice into a real git repo with adopter ignore rules and asserts byte-identical output, adopter lines preserved, framework bytecode ignored, adopter bytecode NOT ignored. (3) backlog #191 `Pending -> In Progress` now, `Shipped` at ship. Rollback: `git revert` the implement commit. Not touched: `deploy.ps1` (delegates to `deploy.sh`, `deploy.ps1:76`), validators (required-pattern list, not exhaustive), CHANGELOG (written at release cut). +- implement: as planned (`ceed4c5`), plus a one-line section comment instead of two to keep the adopter's file short. All three tests mutation-verified. | Confidence: 92% - high +- review: fresh reviewer found no blocker. Adjudicated each claim by reproducing it rather than accepting it: (R1) test outcome depended on the host's global git excludes, reproduced -> fixed with an empty `core.excludesFile`; (R2) mixed-version deploys grow `.gitignore` because an older `deploy.sh` stops stripping at an unknown entry, reproduced +10..13 lines/round -> entry moved last (D-2), now +3/round and 1 stray line on a single downgrade; (N1) no untrack hint in the deploy banner -> closed, would print for every adopter, cleanup goes in release notes; (N2) backlog row #191 text prescribes the repo-wide pair -> rewrite at ship; (N3) PR thread needs the takeover explanation -> planned comment. +- test: full suite caught a 3.9-floor violation in the new test that no targeted run had included; fixed in `52ef638`; see Drift Log and Test Gate Results. +- ship: PASS on `52ef638`; SSoT sequence 170 -> 171, Ship History rotated (`Ship-fix-ignore-assertion-binding-2026-08-24` -> `archive/ship-history-2026.md`); archived to `archive/fix-downstream-ignore-python-bytecode-20260914.md`. Quick-win knowledge nudge: no L2 line, the non-obvious constraints are encoded where the next editor meets them (the `managed[]` comment in `deploy.sh` and the namespace test's assertion messages). + +⚡ ACX + +--- + +## Gate Evidence + +- Gate: bootstrap | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T02:04:28Z +- Gate: plan | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T02:05:10Z +- Gate: implement | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T02:22:51Z +- Gate: review | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T02:46:46Z +- Gate: test | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T04:11:07Z +- Gate: ship | Verdict: PASS | Classification: quick-win | Timestamp: 2026-09-14T04:11:55Z + +--- + +## External References + +| Type | Path / URL | Notes | +|---|---|---| +| Spec | - | - | +| ADR | docs/adr/ADR-005-downstream-file-preservation-tiering.md | framework manages only its own downstream namespace | +| Issue | https://github.com/KbWen/agentic-os/issues/430 | backlog #191 | +| PR | https://github.com/KbWen/agentic-os/pull/435 | contributor PR, taken over in place (maintainerCanModify) | + +--- + +## Known Risk + +- A future framework `.py` shipped outside `.agentcortex/` would not be covered by the scoped line. Mitigation: the planned golden-manifest test fails in that case. +- Adopters who already committed `.agentcortex/tools/__pycache__/` keep it tracked; `.gitignore` does not untrack. Deploy will not run `git rm` in an adopter repo; the release notes should carry the one-line cleanup. +- Mixed framework versions deploying into one repo still add 3 `.gitignore` lines per old->new round; a single downgrade leaves 1 stray line. Caused by older `deploy.sh` code; same class already applied to every earlier entry addition. The "add new entries last" rule is a comment, not a check. + +--- + +## Decisions + +### D-1: framework-scoped pattern instead of repo-wide `__pycache__/` + `*.pyc` + +- **Decision**: ship `.agentcortex/**/__pycache__/` only. +- **Reason**: the repo-wide pair also ignores the adopter's own bytecode anywhere in their tree (measured: `app/legacy.pyc` ignored), a silent policy change caused by installing the framework, and duplicates the rule most Python adopters already have. All 19 deployed `.py` files live in `.agentcortex/tools/`; Python 3 writes bytecode only into `__pycache__/` (PEP 3147), so `*.pyc` adds nothing for framework files. +- **Alternatives**: the submitted two lines plus `managed[]` entries (the reviewer's minimum; fixes growth but keeps the policy change). + +→ local + +### D-2: new block entries go last; no marker-bounded strip in this change + +- **Decision**: emit the entry as the block's last line and say so beside `managed[]`. +- **Reason**: already-deployed `deploy.sh` versions cannot be changed; placement is the only lever over what they leave behind (measured 13 -> 3 lines per alternation round). +- **Alternatives**: strip everything between the markers regardless of the table (reviewer's follow-up). Rejected here: it only helps future downgrades to this version, and it silently deletes lines an adopter added inside the block, which today survive; it also needs a separate rule for legacy blocks with no end marker. Cleaning stray lines above the marker was also rejected: they cannot be told apart from adopter-written lines. + +→ local + +--- + +## Conflict Resolution + +none + +--- + +## Skill Notes + +none + +--- + +## Drift Log + +- Full CI-equivalent suite at `6a3de6e` (951 collected, local 3.14.3, 69m33s, run alone): `1 failed, 949 passed, 1 skipped`, exit 1. Failure was mine: `test_write_text_newline_ratchet` flagged `test_deploy_tiering.py:480 [write_text(newline=) is 3.10+]` in the new double-deploy test (repo floor is 3.9; the ratchet exists for exactly this). Fixed with `write_bytes`. The 3 targeted runs and the mutation runs had not included the ratchet file, which is why only the full suite saw it. +- An earlier full-suite run at `ceed4c5` was stopped at ~15% (contention with the scenario battery and the reviewer, and superseded by review fixes); no result from it is claimed. +- Scope: the reviewer's comment asked only for two `managed[]` lines; D-1 narrows the patterns themselves after the user asked that downstream not be cluttered or given extra trouble. Contributor's commit is kept; changes land as new commits on top. +- Recovered stale Work Log lock on 2026-09-14T04:11:28.482547+00:00; prior_owner=KbWen; prior_session=claude-opus-5-2026-09-14T02:04:28Z; reason=stale-time; lock=fix-downstream-ignore-python-bytecode.lock.json + +--- + +## Review Feedback + +none + +--- + +## Red Team Findings + +- R1 (should-fix, fixed `6a3de6e`): host global excludes decided the adopter-policy assertion. Repro: `GIT_CONFIG_GLOBAL` with `excludesFile` = `__pycache__/` -> `AssertionError: the adopter's own bytecode must be left...`; after fix 3 passed under the same config. +- R2 (should-fix, mitigated `6a3de6e`, residual in Known Risk): mixed-version growth. Repro `scratchpad/mixed_versions.sh`: mid-block `new:36 old:46 old:46 new:49 new:49 old:59 new:62 old:72`; last-in-block `new:36 old:36 old:36 new:39 new:39 old:39 new:42 old:42`. + +--- + +## Design Reference + +none + +--- + +## Observability + +none + +--- + +## Resume + +none + +--- + +## Test Gate Results + +- `python -m pytest tests/ci/ tests/guard/ .agentcortex/tests/ -q` at `6a3de6e`, 951 collected per `--collect-only`, run alone: `1 failed, 949 passed, 1 skipped in 4173.64s`, exit 1 -> `test_write_text_newline_ratchet::test_no_write_text_newline_calls` (own defect). +- After fix `52ef638`: `pytest tests/ci/test_write_text_newline_ratchet.py tests/ci/test_deploy_tiering.py -q` -> `43 passed, 1 skipped in 516.45s`, exit 0. Changed-file rerun, not a second whole-suite run. + +--- + +## Evidence + +- Baseline, PR as submitted at `3667fe6`, 3 real `deploy.sh` runs into fresh git repos (`scratchpad/ignore_sim.sh`): fresh target `.gitignore` lines 33 -> 47 -> 61; Python-adopter target 39 -> 53 -> 67; `__pycache__/` occurs 3x / 4x; managed lines leak outside the block. Adopter `app/legacy.pyc` ignored: yes (policy change). +- Control `origin/main` 924a2dd, same script: 29/29/29 and 35/35/35 lines (idempotent), framework `.pyc` visible to `git status` after `validate.sh`: 2 (the #430 bug). +- `ceed4c5`, same script: 33/33/33 and 39/39/39, adopter lines kept, visible `.pyc` 0, adopter's own `app/legacy.pyc` not ignored. +- Mutation (`ceed4c5`, restored byte-identical via `cmp`): drop `managed[]` entry -> strippable + double-deploy FAIL; repo-wide pair with `managed[]` entries -> namespace + double-deploy FAIL; golden gains `.agents/skills/demo/run.py` -> namespace FAIL. Re-run on `6a3de6e` under a polluted global excludes file: same FAILs for the first two; clean tree 3 passed. +- `pytest tests/ci/test_deploy_tiering.py` at `ceed4c5` -> 41 passed, 1 skipped, exit 0 (read via PIPESTATUS). +- Demonstration, `deploy.ps1` vs `deploy.sh` into fresh targets (ps1 deployed twice): `.gitignore` sha `FFFF383F07` both, pattern present once. +- Scripts named `scratchpad/*.sh` are session-local and not committed; each line records its command shape and result. +- Re-run at `6a3de6e` (entry moved last): battery results identical to the `ceed4c5` line below except `.gitignore` shas; `deploy.ps1` vs `deploy.sh` sha `C52B22DE30` both, block ends with the bytecode entry. +- Downstream battery at `ceed4c5` (`scratchpad/downstream_scenarios.sh`): upgrade from main 33 -> 36 -> 36, validate `pass=86 warn=1 fail=0 skip=8` before and after; committed `.pyc` stay tracked until `git rm -r --cached`, then 0/0; legacy `AI Brain OS` block replaced, idempotent; CRLF adopter file idempotent on main and branch; subdirectory install ignores only its own bytecode, root `.gitignore` untouched; `--no-python` summary identical to main, 0 `.pyc`. diff --git a/.agentcortex/context/archive/ship-history-2026.md b/.agentcortex/context/archive/ship-history-2026.md index 6cf8e860..bc5c55c1 100644 --- a/.agentcortex/context/archive/ship-history-2026.md +++ b/.agentcortex/context/archive/ship-history-2026.md @@ -2,6 +2,17 @@ Archived from `current_state.md ## Ship History` to stay within the 10-entry cap. Entries are rotated out verbatim (per ship.md §205 — never edited), newest-archived first. +### Ship-fix-ignore-assertion-binding-2026-08-24 +- Feature shipped: **the guard that protects the governance record stopped agreeing with `.gitignore` and started asking git.** A sibling fork reported six governance findings against the shared ancestor; diagnosis against this tree confirmed one root cause worth fixing here, at two altitudes. The validators' `.gitignore preserves persistent SSoT artifacts` check compared whole `.gitignore` lines against a fixed list of directory paths — so `.agentcortex/context/archive/*.md`, which hides the archived Work Logs without ever spelling that directory, sailed past it. Reproduced before fixing: one appended line left `validate.sh` at `fail=0` while `git check-ignore` confirmed the logs were hidden. **The same blind spot had grown independently in both forks of the same ancestor**, which is what makes it a class. The check now probes a representative FILE inside each protected artifact (a directory probe reproduces the original blindness, since `docs/specs/*.md` never matches the directory), tri-state so a check that could not run reports SKIP rather than assurance, and is no longer gated on `.gitignore` existing — that branch emitted `.gitignore absent -- no persistent SSoT artifacts are ignored`, a PASS asserted without looking at anything, while `.git/info/exclude` and a global excludes file hide files just as well. +- **Two adopter-facing regressions were introduced and caught before ship, both by an independent reviewer rather than by self-review.** (1) `check-ignore -v` exits 0 whenever a pattern **matched**, negations included: on the ordinary `docs/adr/*` + `!docs/adr/*.md` idiom `-v` exits 0 while `-q` exits 1 and git tracks the file, so reading `-v`'s status as the verdict reds a correct adopter **and names their protective `!` line as the pattern to remove**. (2) `check-ignore` skips **tracked** paths without `--no-index`, so `current_state.md` — the one real path in the probe list — was inert in every healthy deploy: a detection *narrowing* shipped inside a change whose stated purpose was broadening. Both measured, both fixed, both mutation-guarded. A third, found by the scenario sweep: deployed under an outer repository's `vendor/`-style ignore every probe resolves ignored, and per-probe blame points at that outer rule — now diagnosed as its own cause with its own remedy, through the **same** emission site so the ADR-006 native ratchet stays at **204/204**. +- **The playbook's claim is now bound to the mechanism, which is what the report actually asked for.** `audit-guardrails.md` Test 1 asserted `git status` shows none of `.agent/`, `.agents/`, `.antigravity/`, `.agentcortex/context/`; a real cold deploy shows **58 / 29 / 1 / 2** paths, and its command line did not even run (`--force` is not a `deploy.sh` flag; the catch-all `*)` takes it as TARGET). Rewritten to the verified ignore set, pointing the standing guarantee at the validator check, with the heading kept verbatim because it is an encoding canary in both validators. A test now extracts Test 1's own assertion list from the page and executes it against a real deployed ignore block — reintroducing the original drift shape turns it red. Also fixed on the way, all of which ship to adopters: the shell snippet's line continuations had been eaten into one long line, the closing note still named `.gitignore:` after the message was generalised to any ignore source, two wrong glyphs in the zh-TW twin, and an `rm -rf` cleanup that a failed `cd` could point elsewhere. +- **A doc that ships downstream has to be written for the downstream reader.** The first rewrite framed Test 1 around `installers/deploy_brain.sh` and "the source repo root". Measured from an installed project, that wrapper takes its update path: it clones from the remote, writes an `.agentcortex-src/` cache **into the reader's own tree**, and audits the fetched version rather than theirs. The canonical `deploy.sh` exists in both positions, needs no network, and audits the copy on disk. Verified working from the source repo and from a deployed downstream. +- **No claim-decay mechanism, and the reason is the more useful half.** The report's U4 asked for `` markers on quantified claims. The instance found here was **mis-attributed by me** — `pass=87 warn=1 fail=0 skip=6` sits under a dated `### Ship-chore-v1.8.21-release-2026-08-14` heading and was correct for v1.8.21; the 86/1/0/8 measured against v1.8.23 is a different subject. The SSoT's dated sections already supply the as-of anchor U4 says is missing, and a "remember to tag" convention with no verifier is exactly the ritual-without-discriminating-power defect the same report diagnoses (ADR-011 bans unenforced directives). Closed with a reopen trigger, not deferred. +- **17 scenarios across install / update / dev, each run rather than reasoned about** (full matrix in the archived Work Log). Fresh downstream `86/1/0/8` · `--no-python` host `76/1/0/18`, still FAILing on the accident and still passing the negation · non-git tree SKIP `84/2/0/9` · nested under an outer `vendor/` ignore FAIL with its own cause · upgrade from v1.8.23 preserving the downstream's work log, own spec and own ignore rule with tallies **unchanged** at `96/6/2/6` · re-deploy x3 idempotent · rollback to v1.8.23 clean · a `.gitignore` with no trailing newline keeping the adopter's last line · git worktree (`.git` a file) resolving all six probes · the pre-commit hook's real interpreter, **`powershell` 5.1 rather than `pwsh`**, running it end to end · and a **CRLF `.gitignore`**, which is what every `core.autocrlf=true` checkout has. +- **That last scenario existed because the first version of this fix was wrong, and it is the most useful thing in this unit.** The outer-repo branch originally discriminated on `git check-ignore -q --no-index -- .`. A blank CRLF line is not blank to git — it is the pattern `\r`, which git strips to the empty string, and the empty pattern matches the pathspec `.`. So the branch fired on every Windows checkout, including **this repository**, turning a healthy tree red and telling the reader a blank line was the pattern to remove. It survived a self-review, fourteen scenarios and a full green suite, because **every scenario deployed a fresh tree and `deploy.sh` writes LF** — the state was only reachable by letting git check a repo out. An independent reviewer found it by running the validators against this repo rather than against a fixture. Fixed by probing first and only ever **re-labelling**: the outer-repo cause now requires all six probes ignored **and** a non-empty `git rev-parse --show-prefix`, an ordering that cannot turn a PASS into a FAIL. The durable form: **a fresh-deploy fixture is not a checkout.** +- **Adopter delta, measured on one identical tree with each version's own deployed validator**: with `.gitignore` hiding both the archive and the adopter's own spec, **v1.8.23 prints `[PASS]`, `fail=0`**; this build prints `[FAIL] 2/6 probes ignored` naming each `source:line`. On a healthy tree the tallies are identical before and after upgrade, so this adds detection without adding churn. All four changed deployed files are `core` tier, so it arrives on the next deploy with no adopter action. +- Tests: `pytest tests/ci/ tests/guard/ .agentcortex/tests/` with **no `-m` filter**, run as CI runs it (`--splits 3`) after three whole-suite attempts died mid-run and reported a hollow exit 0. Both validators on this repo: exit 0, `pass=118 warn=3 fail=0 skip=2`, **identical**. `validate.ps1` parses and runs clean on 7.6.3 and 5.1.26100.9168. ADR-006 native-check ratchet unmoved at 204/204 — the stricter guard cost no headroom. + ### Ship-chore-release-v1.8.23-2026-08-24 - Feature shipped: **v1.8.23 finishes the residue of a downstream adopter's v1.8.21 report — four units that each unblocked the next.** #175 (`validate.ps1` no longer mojibakes its own `§` and `—` on a non-UTF-8 console; `[Console]::OutputEncoding` saved and restored in a `finally` wrapping the whole script, +15 lines / 0 deletions / no re-indentation) → its bootstrap **reproduced #178 live** (AGENTS.md §Write Isolation forbade the `Pending → In Progress` advance `bootstrap.md §1` step 5 mandates) → fixing #178 **legalised the backlog step #88's bootstrap needed** → #88's committed 897-id `.test_durations` **turned #171 from latent to blocking** (TruffleHog's Lob detector matched the node ids and failed the job outright), forcing it to be fixed before anything could land. Not four independent chores. - **Every headline number is measured, and two were revised downward on measurement rather than published as predicted.** #88's shard rebalance was predicted at 2.2× from workstation-generated weights; it measured **13m7s then 11m24s against a 21m57s baseline = 1.67×–1.93×**, and is recorded as a **range** because two runs of the same tree differed ~13% and choosing the better figure is a claim the data does not support. #175's backlog row claimed the bug "does not reproduce on `pwsh` 7"; measured on the redirected byte stream, 5.1 and 7 emit **identical** bytes, and the row was corrected in place. #88's own row recorded `7:14` for an imbalance that had grown to `21m57s` — stale by ~3×, also corrected. diff --git a/.agentcortex/context/current_state.md b/.agentcortex/context/current_state.md index 1aee7d5c..0938a91a 100644 --- a/.agentcortex/context/current_state.md +++ b/.agentcortex/context/current_state.md @@ -12,9 +12,9 @@ - Active Work Log Path: derive from the raw branch name using filesystem-safe normalization before any gate checks. - Workflows & Policies: `.agent/workflows/*.md`, `.agent/rules/*.md` - **Project Name**: (set by /app-init) -- **Last Updated**: 2026-09-07T14:05:00Z +- **Last Updated**: 2026-09-14T04:11:55Z - **Last Verified**: 2026-09-09 -- **Update Sequence**: 170 +- **Update Sequence**: 171 - **ADR Index**: - docs/adr/ADR-001-governance-friction-tuning.md — ADR-001: Governance Friction Tuning, accepted 2026-04-23 (amended 2026-07-16: `design_tool` capability-seam escape rejected — D2 reaffirmed, do NOT retry) - docs/adr/ADR-002-guarded-governance-writes.md — ADR-002: Guarded Governance Writes (lock unification + CI lint + lifecycle frontmatter), accepted 2026-04-25 @@ -113,6 +113,14 @@ - [Category: skill-description-cost][Severity: HIGH][Trigger: editing-skill-md][prev: 3edf8155] Every character added to a .agents/skills/*/SKILL.md costs about 2.33 tokens against the aggregate lifecycle ceiling, roughly 8.9x its own size: analyze_token_lifecycle.py counts the whole file once per scenario the skill is a candidate for, plus first-load and continuation. Headroom was 431 before PR #437 and is 113 after it. On #437, rewriting two descriptions to the app-init.md:200 standard reached 355225 against the 355000 ceiling while the targeted tests, both validators and two review rounds all stayed green - only the full suite caught it. Before editing any SKILL.md, measure with analyze_token_lifecycle.py --root . --format json. The conflict between that description contract and this ceiling is open as backlog #199 and needs an owner decision before further skill-description work, including #198. ## Ship History +### Ship-fix-downstream-ignore-python-bytecode-2026-09-14 + +- Feature shipped: **contributor PR #435 (issue #430, backlog #191) finished in place, with the owner's approval, after a week without response to review.** The deployed `.gitignore` block now carries `.agentcortex/**/__pycache__/`, so running the framework's own `validate.sh` no longer leaves bytecode for the banner's `git add .agentcortex/` to stage. The PR as submitted added repo-wide `__pycache__/` + `*.pyc` without `managed[]` entries: every re-deploy grew the adopter's file by 14 lines (measured 33 -> 47 -> 61), and the repo-wide pair also ignored the adopter's own bytecode. What shipped: scoped to the framework namespace (all 19 deployed `.py` are under `.agentcortex/tools/`), in `managed[]`, written last in the block. Contributor commit kept; no force-push. +- **Adopter delta, measured on real deploys against `origin/main`**: fresh install, framework `.pyc` visible to `git status` after `validate.sh` 2 -> 0; upgrade adds 3 lines, then byte-identical on every re-deploy; adopter's own lines and own bytecode policy untouched; validator tallies identical (`86/1/0/8`, `--no-python` `76/1/0/18`). Unchanged: bytecode an adopter already committed stays tracked. The one-time `git rm -r --cached .agentcortex/tools/__pycache__` belongs in the next release notes; deploy does not run git commands in the adopter's repository. +- **Downstream scenarios were run, not reasoned about**: upgrade from main with committed bytecode, legacy `AI Brain OS` block, CRLF adopter file, subdirectory install in a monorepo, no-Python, and `deploy.ps1` vs `deploy.sh` byte parity. Matrix in the archived Work Log. +- **The fresh reviewer (same-vendor subagent) found two defects, and both were reproduced before being fixed.** (1) The new double-deploy test read the developer's global git excludes, so it failed on any machine that ignores `__pycache__/` globally. (2) Mixed framework versions deploying into one repository grew `.gitignore` by 10-13 lines per round, because an older `deploy.sh` stops stripping at the first entry it does not know. Writing the entry last cut that to 3 lines per round. The remainder lives in older-version code and is recorded, not claimed fixed. A marker-bounded strip was rejected because it would delete lines adopters added inside the block. +- Tests: Pass. The full CI-equivalent suite (`tests/ci/ tests/guard/ .agentcortex/tests/`, 951 collected) ran alone at `6a3de6e`: **949 passed, 1 skipped, 1 failed**. The failure was this unit's own: the new test used `write_text(newline=)`, which is 3.10+, and `test_write_text_newline_ratchet` caught it against the 3.9 floor. None of the targeted or mutation runs had included that ratchet. Fixed in `52ef638`, after which the ratchet plus `test_deploy_tiering.py` came to 43 passed, 1 skipped. This is a whole-suite run followed by a changed-file rerun, not one clean run, and it is recorded as such. Validator figures are in the archived Work Log §Final Verification. + ### Ship-docs-retro-skill-description-lessons-2026-09-10 - Feature shipped: `/retro` for PR #437. Its durable learnings had been kept in Claude-private memory, which Codex and Gemini cannot read; the user corrected that, so they now live in the repo's own records. One Global Lesson added - `[skill-description-cost][HIGH][editing-skill-md]`: each SKILL.md character costs ~2.33 tokens against a ceiling with 113 headroom, targeted checks stay green on a breaching tree, and backlog **#199** is the open decision - so it now surfaces in the `/implement` HIGH-lesson review on every host rather than only in one machine's memory. `repo-gotchas §16` extended with the host-side consumer: Codex selects on the SKILL.md `description`; `openai.yaml` is UI metadata; Claude Code does not read `.agents/skills` here. @@ -183,15 +191,3 @@ ### Ship-main-2026-08-27 - Feature shipped: **v1.8.25 closes the release-to-downstream consistency gap.** Backlog #182 adds a pytest guard that derives the canonical version from `deploy.sh`, pins every release surface plus citation-date ordering, and was proved red against the v1.8.24 runtime-guide drift before repair. Companion backlog #180 excludes `.claude` from bare pytest discovery, eliminating duplicate collection from leftover agent worktrees. The adversarial premortem and clean-downstream reproduction are preserved in `docs/reviews/2026-08-26-govern-audit-release-downstream-premortem.md`; implementation commit `6984551`. - Tests: Pass — release consistency + deploy-manifest snapshot 3 passed; bare collection 916 tests / 0 errors; final post-archive validators and CI-equivalent suite recorded in `.agentcortex/context/archive/main-20260827.md`. - -### Ship-fix-ignore-assertion-binding-2026-08-24 -- Feature shipped: **the guard that protects the governance record stopped agreeing with `.gitignore` and started asking git.** A sibling fork reported six governance findings against the shared ancestor; diagnosis against this tree confirmed one root cause worth fixing here, at two altitudes. The validators' `.gitignore preserves persistent SSoT artifacts` check compared whole `.gitignore` lines against a fixed list of directory paths — so `.agentcortex/context/archive/*.md`, which hides the archived Work Logs without ever spelling that directory, sailed past it. Reproduced before fixing: one appended line left `validate.sh` at `fail=0` while `git check-ignore` confirmed the logs were hidden. **The same blind spot had grown independently in both forks of the same ancestor**, which is what makes it a class. The check now probes a representative FILE inside each protected artifact (a directory probe reproduces the original blindness, since `docs/specs/*.md` never matches the directory), tri-state so a check that could not run reports SKIP rather than assurance, and is no longer gated on `.gitignore` existing — that branch emitted `.gitignore absent -- no persistent SSoT artifacts are ignored`, a PASS asserted without looking at anything, while `.git/info/exclude` and a global excludes file hide files just as well. -- **Two adopter-facing regressions were introduced and caught before ship, both by an independent reviewer rather than by self-review.** (1) `check-ignore -v` exits 0 whenever a pattern **matched**, negations included: on the ordinary `docs/adr/*` + `!docs/adr/*.md` idiom `-v` exits 0 while `-q` exits 1 and git tracks the file, so reading `-v`'s status as the verdict reds a correct adopter **and names their protective `!` line as the pattern to remove**. (2) `check-ignore` skips **tracked** paths without `--no-index`, so `current_state.md` — the one real path in the probe list — was inert in every healthy deploy: a detection *narrowing* shipped inside a change whose stated purpose was broadening. Both measured, both fixed, both mutation-guarded. A third, found by the scenario sweep: deployed under an outer repository's `vendor/`-style ignore every probe resolves ignored, and per-probe blame points at that outer rule — now diagnosed as its own cause with its own remedy, through the **same** emission site so the ADR-006 native ratchet stays at **204/204**. -- **The playbook's claim is now bound to the mechanism, which is what the report actually asked for.** `audit-guardrails.md` Test 1 asserted `git status` shows none of `.agent/`, `.agents/`, `.antigravity/`, `.agentcortex/context/`; a real cold deploy shows **58 / 29 / 1 / 2** paths, and its command line did not even run (`--force` is not a `deploy.sh` flag; the catch-all `*)` takes it as TARGET). Rewritten to the verified ignore set, pointing the standing guarantee at the validator check, with the heading kept verbatim because it is an encoding canary in both validators. A test now extracts Test 1's own assertion list from the page and executes it against a real deployed ignore block — reintroducing the original drift shape turns it red. Also fixed on the way, all of which ship to adopters: the shell snippet's line continuations had been eaten into one long line, the closing note still named `.gitignore:` after the message was generalised to any ignore source, two wrong glyphs in the zh-TW twin, and an `rm -rf` cleanup that a failed `cd` could point elsewhere. -- **A doc that ships downstream has to be written for the downstream reader.** The first rewrite framed Test 1 around `installers/deploy_brain.sh` and "the source repo root". Measured from an installed project, that wrapper takes its update path: it clones from the remote, writes an `.agentcortex-src/` cache **into the reader's own tree**, and audits the fetched version rather than theirs. The canonical `deploy.sh` exists in both positions, needs no network, and audits the copy on disk. Verified working from the source repo and from a deployed downstream. -- **No claim-decay mechanism, and the reason is the more useful half.** The report's U4 asked for `` markers on quantified claims. The instance found here was **mis-attributed by me** — `pass=87 warn=1 fail=0 skip=6` sits under a dated `### Ship-chore-v1.8.21-release-2026-08-14` heading and was correct for v1.8.21; the 86/1/0/8 measured against v1.8.23 is a different subject. The SSoT's dated sections already supply the as-of anchor U4 says is missing, and a "remember to tag" convention with no verifier is exactly the ritual-without-discriminating-power defect the same report diagnoses (ADR-011 bans unenforced directives). Closed with a reopen trigger, not deferred. -- **17 scenarios across install / update / dev, each run rather than reasoned about** (full matrix in the archived Work Log). Fresh downstream `86/1/0/8` · `--no-python` host `76/1/0/18`, still FAILing on the accident and still passing the negation · non-git tree SKIP `84/2/0/9` · nested under an outer `vendor/` ignore FAIL with its own cause · upgrade from v1.8.23 preserving the downstream's work log, own spec and own ignore rule with tallies **unchanged** at `96/6/2/6` · re-deploy x3 idempotent · rollback to v1.8.23 clean · a `.gitignore` with no trailing newline keeping the adopter's last line · git worktree (`.git` a file) resolving all six probes · the pre-commit hook's real interpreter, **`powershell` 5.1 rather than `pwsh`**, running it end to end · and a **CRLF `.gitignore`**, which is what every `core.autocrlf=true` checkout has. -- **That last scenario existed because the first version of this fix was wrong, and it is the most useful thing in this unit.** The outer-repo branch originally discriminated on `git check-ignore -q --no-index -- .`. A blank CRLF line is not blank to git — it is the pattern `\r`, which git strips to the empty string, and the empty pattern matches the pathspec `.`. So the branch fired on every Windows checkout, including **this repository**, turning a healthy tree red and telling the reader a blank line was the pattern to remove. It survived a self-review, fourteen scenarios and a full green suite, because **every scenario deployed a fresh tree and `deploy.sh` writes LF** — the state was only reachable by letting git check a repo out. An independent reviewer found it by running the validators against this repo rather than against a fixture. Fixed by probing first and only ever **re-labelling**: the outer-repo cause now requires all six probes ignored **and** a non-empty `git rev-parse --show-prefix`, an ordering that cannot turn a PASS into a FAIL. The durable form: **a fresh-deploy fixture is not a checkout.** -- **Adopter delta, measured on one identical tree with each version's own deployed validator**: with `.gitignore` hiding both the archive and the adopter's own spec, **v1.8.23 prints `[PASS]`, `fail=0`**; this build prints `[FAIL] 2/6 probes ignored` naming each `source:line`. On a healthy tree the tallies are identical before and after upgrade, so this adds detection without adding churn. All four changed deployed files are `core` tier, so it arrives on the next deploy with no adopter action. -- Tests: `pytest tests/ci/ tests/guard/ .agentcortex/tests/` with **no `-m` filter**, run as CI runs it (`--splits 3`) after three whole-suite attempts died mid-run and reported a hollow exit 0. Both validators on this repo: exit 0, `pass=118 warn=3 fail=0 skip=2`, **identical**. `validate.ps1` parses and runs clean on 7.6.3 and 5.1.26100.9168. ADR-006 native-check ratchet unmoved at 204/204 — the stricter guard cost no headroom. - diff --git a/docs/specs/_product-backlog.md b/docs/specs/_product-backlog.md index 0364ea74..2a01bb8e 100644 --- a/docs/specs/_product-backlog.md +++ b/docs/specs/_product-backlog.md @@ -3,7 +3,7 @@ status: living title: Product Backlog source: governance-bloat-review-2026-04-12 + optimization-round-2026-05-04 + optimization-research-2026-06-02 created: 2026-04-12 -last_updated: 2026-09-09 +last_updated: 2026-09-14 --- # Product Backlog @@ -156,7 +156,7 @@ Governance file bloat review (2026-04-12) identified 10 findings across P0–P2: | 188 | Brownfield FIRST install silently destroys pre-existing files on all 142 core-tier paths - verified 2026-09-05. `deploy.sh` guards the *update* branch (locally-modified files are sidecar-preserved to `.acx-incoming`) and correctly `[SKIP]`s the three platform entry points, but on a first install into a project that already has a file at a core-tier path it overwrites in place: no `.acx-incoming`, no backup, **zero mention in the deploy output**, exit 0. Reproduced with an untracked `.agent/rules/engineering_guardrails.md` - sentinel gone, `git log` on the path returns 0 commits, so the content is unrecoverable; a tracked file survives only via `git diff`, which the banner never tells the adopter to check. `grep -c '^core ' .agentcortex-manifest` = 142 exposed paths, including all of `.claude/commands/*.md` and `.agent/rules/*`. Highest-blast-radius downstream defect in this batch. ADR-005 documents skills-to-sidecar and framework-authoritative-to-force-update but has **no brownfield-first-install case**, so the fix amends the ADR's preservation contract rather than patching one branch - needs a spec, not a quick patch. Related: #193 (same scenario, banner-honesty half). | framework | deploy | P1 | - | feature | Pending | - | - | | 189 | The Active Backlog's advertised read cost is understated ~170x - measured 2026-09-05. `AGENTS.md:36` tells every agent `_product-backlog.md` is a `Free-read (~200 tokens)`; the file is 139,206 chars = **~34,800 tokens**. The documented mitigation does not mitigate: `bootstrap.md:171` says to read *only* the Feature Inventory table, and that table is 137,998 chars - **99.1% of the file**. The same `~200 tokens` figure is repeated at `spec-intake.md:64`, `:349` and `:362`, so four call sites are wrong together. Consequence: an agent following `AGENTS.md` at bootstrap believes it is spending 200 tokens and spends ~35k - on the one growth surface `check_ssot_caps.py` does **not** cover (it caps only Ship History, currently 10/10, and Spec Index, 27/30, both inside `current_state.md`). Fix is two-part and the second half is the real work: correct the four figures (trivial), then decide whether the backlog gets a cap/rotation like the SSoT sections - which overlaps #1/#3/#11 and must not be re-solved independently. | docs | governance | P2 | - | quick-win | Pending | - | #1 | | 190 | Both validator twins ignore `.agent/config.yaml` `downstream_capabilities.path` and hardcode the default - verified 2026-09-05. `config.yaml:112` publishes `path:` as an adopter-settable key and `bootstrap.md:116` explicitly instructs the agent to check that key before loading; but `validate.sh:647` (`CAP_FILE=`) and `validate.ps1:791` (`$capFile`) both pin `.agentcortex/context/private/downstream-capabilities.yaml` literally. An adopter who retargets `path:` therefore gets the documented behaviour from the AI (it reads the retargeted file) and a silent green from the gate-safety validator (it inspects a path that no longer exists and reports SKIP/PASS). That is the ADR-007 invariant - gate-relaxation must be *unrepresentable* - being bypassable via a supported config edit. Scope note: the caps themselves (`load_policy_ceiling`, `custom_prefix_required`) are enforced correctly by `validate_downstream_capabilities.py`; only the **path resolution** is unwired, so the fix is small and must land in both twins together (cross-platform parity). | framework | governance | P1 | - | quick-win | Pending | - | - | -| 191 | The deployed `.gitignore` block omits `__pycache__/` and `*.pyc`, so the framework's own tools dirty the adopter's tree - verified 2026-09-05. The source repo protects itself at `.gitignore:50-51`; the block `deploy.sh` writes downstream does not carry those two lines. Running the framework's own documented self-check (`bash .agentcortex/bin/validate.sh`) in a fresh target immediately creates `.agentcortex/tools/__pycache__/*.pyc`, and the deploy banner's own "Finish setup" line (`git add .agentcortex-manifest AGENTS.md ... .agentcortex/ ...`) stages them. An adopter who follows the printed instructions in the printed order commits Python bytecode on their first framework commit. Smallest correct fix is additive and unambiguous: add the two patterns to the downstream ignore-defaults block in `deploy.sh` (and the PowerShell path), then extend the deploy-tiering test that pins the ignore block. Filed separately from #188/#193 on purpose - it is the one finding in this batch with no design question attached. | framework | deploy | P2 | - | quick-win | In Progress | [#430](https://github.com/KbWen/agentic-os/issues/430) | - | +| 191 | The deployed `.gitignore` block omits `__pycache__/` and `*.pyc`, so the framework's own tools dirty the adopter's tree - verified 2026-09-05. The source repo protects itself at `.gitignore:50-51`; the block `deploy.sh` writes downstream does not carry those two lines. Running the framework's own documented self-check (`bash .agentcortex/bin/validate.sh`) in a fresh target immediately creates `.agentcortex/tools/__pycache__/*.pyc`, and the deploy banner's own "Finish setup" line (`git add .agentcortex-manifest AGENTS.md ... .agentcortex/ ...`) stages them. An adopter who follows the printed instructions in the printed order commits Python bytecode on their first framework commit. Filed separately from #188/#193 on purpose - it is the one finding in this batch with no design question attached. **Shipped 2026-09-14 (PR #435), narrower than first proposed here:** one framework-scoped line `.agentcortex/**/__pycache__/` instead of the repo-wide `__pycache__/` + `*.pyc` pair, which would also have ignored the adopter's own bytecode. It is in the `managed[]` strip table (without it every re-deploy grew the file by 14 lines) and written last in the block, so an older `deploy.sh` leaves at most one stray line. `deploy.ps1` delegates to `deploy.sh` and needed no change. Bytecode an adopter already committed stays tracked until `git rm -r --cached`; that belongs in release notes. | framework | deploy | P2 | - | quick-win | Shipped | [#430](https://github.com/KbWen/agentic-os/issues/430) | - | | 192 | `.agent/rules/repo-gotchas.md` ships to adopters citing this repo's own undeployed paths - verified 2026-09-05. It is a `core` manifest entry and is routed to from the `AGENTS.md` Repo Gotchas reference line, so a downstream agent is told to consult it before touching those surfaces. Four **full-path** citations do not exist in a deployed tree: `.agentcortex/tests/test_lifecycle_token_consumption.py`, `tests/ci/test_deploy_tiering.py`, `tests/ci/test_pre_commit_hook.py`, `tests/ci/test_subprocess_encoding.py` (`tests/` is not in the deploy set). Bare-basename citations in the same file were **excluded** from this count - they are the exact false-positive class #185 describes, and quoting a combined total would reproduce the very defect #185 is about. The file also carries this repo's PR numbers and its own release ritual, which are meaningless downstream. Resolve by settling scope first: either stop shipping it (DELETE - it is arguably an upstream-maintainer doc), or split the genuinely portable traps from the repo-local ones. Do not simply fix the links. | docs | adoption | P3 | - | quick-win | Pending | - | #185 | | 193 | A brownfield install ends validator-RED while the deploy banner reports success - verified 2026-09-05. In the same target as #188, `bash .agentcortex/bin/validate.sh` exits 1 with `pass=83 warn=1 fail=3 skip=8`: `[FAIL] safety nucleus freshness`, `[FAIL] work log contract references are stale`, and `[FAIL] AGENTS.md missing routing index reference (authority handoff absent)`. The third is load-bearing - `deploy.sh` correctly `[SKIP]`s the adopter's own `AGENTS.md`/`CLAUDE.md`, and the very same run then prints `Platform Entry Points Ready: AGENTS.md / CLAUDE.md / GEMINI.md`. The adopter is told the entry points are wired at the moment the validator's own verdict is that the authority handoff is absent: the framework is on disk with zero governance reach, and nothing in the deploy output says so. This is the **honesty half** of #188 and has a much smaller fix than the preservation half - the banner should report what the run actually did (skipped vs installed) and point at the merge step, or the deploy should end by running the self-check instead of asserting readiness. Same axis as the downstream-signal-honesty work already shipped in `dev-flow-hardening`. | framework | adoption | P2 | - | quick-win | Pending | - | #188 | | 194 | **Downstream-audit coverage gap — 8 of 16 planned scenarios never ran (2026-09-05).** The pass that produced #188-#193 was halted early to conserve budget, so these surfaces are **unaudited, not clean**, and no later row should be read as clearing them: (a) *zero-Python* — whether a check that could not run reports as PASS, vanishes from the summary, or leaves the exit code green; also whether `credential_floor.sh`/`.ps1` actually executes with Python absent. **Highest-value gap: this is the one surface with a plausible silent false-pass.** (b) *re-deploy/upgrade* — idempotency (a no-change second deploy must report SKIP, not "updated"), local-modification tiering, orphan removal, stale-manifest handling. (c) *Windows-native* — space-in-path targets, the `compute_sha256` leading-backslash hazard (a broken hash comparison shows up as everything being "updated" on an unchanged tree), CRLF on deployed `.sh`. (d) *non-git and nested-in-parent-repo targets* — whether validators walk up to the outer repo; worklog-key derivation on a detached HEAD or a branch name illegal as a Windows filename. (e) *greenfield* — virgin-install validator noise; manifest-vs-disk spot check. (f) *validator parity* — `validate.sh` (3105 lines) vs `validate.ps1` (2895) has never been diffed check-for-check; the delta is unexplained and may be missing coverage rather than shell verbosity. (g) *skill shipping* — whether `.agents/skills/*/SKILL.md` bodies actually reach a deployed tree (a stub layer without bodies would mean every skill silently resolves to nothing downstream). (h) *gate-bypass battery* — phase skipping, a `NOT READY` receipt satisfying ship, forged guard receipts, lock stealing, `INDEX.jsonl` middle-row edit and tail truncation. **Resume recipe — plain shell, no orchestration tooling required.** Each scenario is independent; deploy once into a scratch dir and work there. Setup: `mkdir -p /tmp/acx/t && cd /tmp/acx/t && git init -q . && echo x > f.txt && git add -A && git commit -qm i && bash /.agentcortex/bin/deploy.sh .` Then for (a): confirm Python is unreachable first with `env PATH=/usr/bin:/bin bash -lc 'command -v python python3 py'`, then run `env PATH=/usr/bin:/bin bash .agentcortex/bin/validate.sh` and diff its per-check output against a normal run - an honest visible `SKIP`/`WARN` that is counted in the summary is CORRECT; a PASS, a silent disappearance, or a green exit is the defect. For (b): deploy twice with no changes and read the counters. For (f): extract the check labels from each twin and diff the sets. **Do NOT try to resume the original run** - `resumeFromRunId` is same-session-only and that session is closed; the raw returns of the 8 scenarios that DID complete are in that run's `journal.jsonl` under `~/.claude/projects///subagents/workflows/wf_15af2b9b-3f2/` on the machine that ran it, and are not portable. **Also unfiled on purpose**: those 8 completed reports contained ~12 further findings the primary agent did not reproduce; they were withheld per the evidence-before-adding norm, not lost. Recurring themes worth re-deriving from scratch: the deploy banner's `git add` line omitting `.gitignore`/`.githooks/`/`.gitattributes` (hit by 3 independent agents); `security_guardrails.md` section 6's "ship gate = FAIL" labelled enforced with no mechanism located; the token ceiling being enforced by zero *required* status checks; `.agentcortex/docs/README.md` shipping as the repo-root README and dangling its intra-doc links downstream. Treat each as an unverified lead requiring its own reproduction before filing. Suggested order: (a) first, then #190 (smallest real fix), then (f). Related: #188-#193 (what the same pass did verify). | framework | governance | P2 | - | quick-win | Pending | - | #188, #190 | From ecd74ae5b19519ee453368a80eecc1ed2eab9b36 Mon Sep 17 00:00:00 2001 From: KbWen Date: Mon, 14 Sep 2026 13:19:32 +0800 Subject: [PATCH 6/6] docs(ship): record final verification for #435 Co-Authored-By: Claude Opus 5 (1M context) --- .../fix-downstream-ignore-python-bytecode-20260914.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md b/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md index 7f8c5dd2..90758f6e 100644 --- a/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md +++ b/.agentcortex/context/archive/fix-downstream-ignore-python-bytecode-20260914.md @@ -179,3 +179,13 @@ none - Scripts named `scratchpad/*.sh` are session-local and not committed; each line records its command shape and result. - Re-run at `6a3de6e` (entry moved last): battery results identical to the `ceed4c5` line below except `.gitignore` shas; `deploy.ps1` vs `deploy.sh` sha `C52B22DE30` both, block ends with the bytecode entry. - Downstream battery at `ceed4c5` (`scratchpad/downstream_scenarios.sh`): upgrade from main 33 -> 36 -> 36, validate `pass=86 warn=1 fail=0 skip=8` before and after; committed `.pyc` stay tracked until `git rm -r --cached`, then 0/0; legacy `AI Brain OS` block replaced, idempotent; CRLF adopter file idempotent on main and branch; subdirectory install ignores only its own bytecode, root `.gitignore` untouched; `--no-python` summary identical to main, 0 `.pyc`. + +## Final Verification + +> Sole location of this unit's closing figures, taken against `a45f4f6` (tree clean) after every other write. + +- `validate.sh` exit 0 and `validate.ps1` exit 0: both `pass=99 warn=4 fail=0 skip=3`, identical; all 4 WARNs pre-existing (none name this log) +- 19 SSoT/backlog/chain-sensitive test files + the 3.9 ratchet -> `388 passed`, exit 0 +- `check_audit_chain.py` intact; `check_ssot_caps.py` ship history 10/10 + +⚡ ACX