Skip to content

Task 0: deterministic listing probes for both deployable harnesses - #56

Merged
davisbuilds merged 4 commits into
mainfrom
profiles/task-0-probes
Aug 3, 2026
Merged

Task 0: deterministic listing probes for both deployable harnesses#56
davisbuilds merged 4 commits into
mainfrom
profiles/task-0-probes

Conversation

@davisbuilds

Copy link
Copy Markdown
Owner

First task of the distribution-profiles plan: the read-only probes every later task trusts. Task 0 is the plan's stop gate — nothing downstream is worth anything if the listing measurement is subtly wrong.

Adds scripts/profiles/{probe_codex,probe_claude}.py, six fixtures, and 27 hermetic tests. Suite 313 → 340. No mutation: both probes re-run on demand, so staleness is checked rather than assumed.

It works

Codex  (dojo): 41 entries, 2 namespaced, 4,132 tok = 76.0% of 5,440, absolute render mode
Claude (dojo): 94 loaded → 75 sent, demand 23,287 vs 8,000 budget, 52 descriptions removed

Four findings that change later tasks

  1. Only skill lines are charged against the Codex budget. render.rs sums line_cost over entries; intro prose and section headers are not counted. Every figure this program has produced charged the whole block and overstated by ~2 points. Corrected in spec and plan: ordinary session 76% (not 78%), viral 95% (not 97%).
  2. The alias roots table is a rounded difference of two whole bodies, not a sum of per-line costs — 24 tokens against 65 on the live fixture. The sum shrinks the apparent limit by 41 tokens, enough to misreport a listing that exactly fits. Task 3 must port aliased_metadata_overhead_cost, not approximate it.
  3. Codex has a third degradation tier nobody had recorded: whole-skill omission, and unlike truncation it does emit a warning into the prompt. Task 3 needs three Codex shapes, not one.
  4. Codex reports resolved paths. A project root symlinked into the catalog — how every dojo checkout exposes itself — appears in the roots table as the canonical path, so comparing against the unresolved cwd finds project scope nowhere and returns a confident zero. Task 4 must resolve.

Five more are in the plan's Task 0 "Executed" block, including that the plan's stated reason for reading messages was wrong: dojo's ## Available Skills decoy lives in messages too, so the section doesn't discriminate — only the literal opening sentence does. Right conclusion, wrong reason; the fixture retains both to pin it.

Vendor parity is exact, not approximate

On a capture that truncated, the port's entry cost equals limit − table_cost to the token (5,416 = 5,416), because render_lines_with_description_budget spends the description budget down to the last one. An off-by-one anywhere in the byte counting, the newline, or the ceiling division breaks it. Asserted as a test.

The mutation probe caught one that review would not have

Five deliberate breakages introduced against the detectors. Four failed immediately. One passed all 23 tests: setting sent := loaded — the filesystem-versus-context error in a new costume — because the over-budget warning line also carries a skill count and was silently overwriting sent. So sent was only ever proven on over-budget captures, which is most of them.

Fixed by keeping the two counts separate and cross-checking them, and by capturing a 1M-window session that emits no warning at all, which isolates the sent path. That fixture doubles as a direct demonstration of SC-03: identical catalog, same machine, same day — over budget on 200k, silent on 1M.

Two portability defects caught before CI

Both would have passed locally and failed every CI run — the docs/archive false-pass shape from R17 again:

  • Tests referenced the capture directory by absolute path. Now rebuilt under tmp_path with a deliberately dangling symlink, since the target path is what classification compares against and it belongs to the machine that produced the capture.
  • fingerprint() crashed when the harness binary was absent. Now an unknown version, which is a legitimate stale state.

Fixture hygiene

The --debug-file captures were committed raw at 252 lines, of which the parser reads 4. The remainder carried permission allow-rule lists from both settings scopes, MCP server names, and hook configuration — machine configuration with no business in a fixture, and hard to remove once in history. Trimmed to the six skill-resolution lines with a header noting what was cut (37 KB → 1.3 KB). The other four fixtures were audited and are clean; the API_KEY hits in the Codex captures are env-var names inside skill descriptions, never values.

A correction to the spec

Earlier in the day I wrote that no live foreign entry remained and the SC-05 fixture would have to be constructed — reasoning from microsoft-foundry being disabled to a general absence, without running the probe. The probe reports spreadsheet, listed in every session. Both the error and the correction are recorded in the spec and plan, since inferring an absence one command away from the instrument is this contract's own subject matter.

Verification

  • pytest tests/ -q340 passed
  • validate_spec.py PASS, validate_plan.py PASS, validate_skill_contract.py --strict PASS
  • Hermetic: passes with the capture directory deleted, from a foreign cwd, and with PATH stripped of both harness binaries

🤖 Generated with Claude Code

davisbuilds and others added 2 commits August 2, 2026 21:48
Adds scripts/profiles/{probe_codex,probe_claude}.py, six captured fixtures, and
27 hermetic tests. Suite 313 -> 340. Read-only: both probes re-run on demand, so
staleness is checked rather than assumed.

Four findings change later tasks and are recorded in full in the plan's Task 0
"Executed" block, with the rest:

- Only skill lines are charged against the Codex budget, so every figure this
  program has produced overstated by ~2 points. Corrected in spec and plan.
- The alias roots table is a rounded difference of two whole bodies, not a sum
  of lines (24 tokens against 65) -- Task 3 must port it, not approximate it.
- Codex has a third degradation tier, whole-skill omission, which does emit a
  prompt warning where truncation does not. Task 3 needs three shapes.
- Codex reports resolved paths, so project scope must be compared resolved or
  a symlinked root returns a confident zero. Task 4.

Debug fixtures are trimmed to the six skill-resolution lines the parser reads.
The raw --debug-file output is 252 lines and also carries permission allow-rule
lists from both settings scopes, MCP server names, and hook configuration --
machine configuration with no business in a fixture, and hard to remove once it
is in history.

Parity with render.rs is exact: on a truncating capture, entry cost equals
limit minus table cost to the token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI caught this; local runs could not. `classify` built its plugin and
`.system` needles from `Path.home()`, which matched the machine that produced
the fixtures and nothing else. On any other host the needles never matched, so
plugin detection returned zero and Codex's own bundled skills were filed as
dojo-managed -- a confident wrong answer with no error, which is the exact
failure mode these probes exist to prevent.

The home is now inferred from the listing's own locators; every Codex listing
carries its `.system` skills, so one is always present. An undiscoverable home
raises rather than classifying, because otherwise every origin falls through
silently.

Two tests pin it: classification with a deliberately wrong home finds no plugin
entries while the inferred one does, and an empty listing refuses.

The earlier hermeticity check varied cwd and PATH but not HOME, which is where
the coupling was. Verified now under all three: 342 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@davisbuilds

Copy link
Copy Markdown
Owner Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cf1539ca9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/profiles/probe_codex.py Outdated
Comment thread scripts/profiles/probe_claude.py Outdated
Comment thread scripts/profiles/probe_claude.py
Comment thread scripts/profiles/probe_codex.py Outdated
davisbuilds and others added 2 commits August 2, 2026 22:09
All four review findings verified against the artifact and fixed. Three were
real breakage; the fourth was a label asserting more than the evidence.

P1 -- the Claude parser dropped six entries. `[^:]+?` for the name could not
match a bare namespaced entry like `- workflows:brainstorm`, so 75 listed
became 69 parsed and every plugin skill vanished. This is the same defect the
Codex parser has a dedicated test for, made a second time in the same module.
The separator is a colon followed by a space; a bare colon belongs to the name.
`parse_request` now raises when parsed count differs from listing-line count,
so a drop fails loudly instead of returning a short list that looks whole.

P1 -- neither live probe classified. `probe()` and `request()` returned parsed
output directly, so every CLI invocation and every downstream caller saw
`origin="unknown"` for all entries, which reads as "no plugin entries, no
foreign entries". The tests passed because they called `classify` themselves.
Both live paths now classify, and a stubbed-subprocess test pins it.

P1 -- Claude entries had no scope and no origin resolution. Added, joining
listed names against the canonical catalog and the two roots. The listing
decides membership; the filesystem only labels entries already listed.

P2 -- utilization mixed units. With no resolvable context window, render.rs
falls back to an 8,000-character budget while the cost stayed in tokens,
displaying 51.6% for a listing whose honest character utilization is 205%.
Utilization is now computed in the budget's own unit and refuses an unknown
one, and an indeterminate model prints an explicit unsupported warning.

Also: the assertion that let the dropped entries through was `len(names) > 10`
on a 75-entry listing. Replaced with conservation against the block itself.

And the Claude fallback origin is now `unresolved` rather than
`harness-bundled` -- Claude's listing carries no locators, so that label
asserted an identification the evidence does not support. Reconciling the
bucket against probe A's source counts is Task 4's.

Suite 342 -> 348. Verified under foreign HOME, stripped PATH, and foreign cwd.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This repository is public and the probe fixtures are verbatim harness output,
so they carried absolute paths and one capture from an unrelated project.

Removed the viral-rooted Codex capture. It was declared as a pytest fixture,
used by no test, and published six skill names with full descriptions
belonging to a private project. Captures belong here only when rooted in this
repository or in a synthetic directory.

Pseudonymised the home directory to /Users/example-dev across the remaining
fixtures. The replacement is deliberately the same byte length as the original:
the vendor-parity test asserts that a truncating listing fills its budget
exactly, so any length change breaks that equality -- a shorter placeholder was
tried first and failed the test immediately, which is the assertion working.

Scrubbed absolute clone and scratchpad paths from the plan, including a session
UUID.

Two guard tests so a future capture cannot reintroduce either problem: fixtures
must carry no real machine identity, and no fixture may be a capture of another
project.

Audited what remains and it is all vendor-shipped or already public: dojo's own
skills, Codex's bundled entries, Claude Code's bundled skills, and two public
marketplace plugins. The entries that looked personal turned out to ship inside
the Claude Code binary, which is why the classifier files them as unresolved.

Suite 348 -> 350.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@davisbuilds
davisbuilds merged commit 17de1b0 into main Aug 3, 2026
1 check passed
@davisbuilds
davisbuilds deleted the profiles/task-0-probes branch August 3, 2026 02:38
davisbuilds added a commit that referenced this pull request Aug 3, 2026
#57)

Docs only — no code changes. Follows Task 0 (#56).

## The change

SC-03 required the fits-proof against **every pair whose limit could be
established**, which silently promoted Claude Code at 200k to a gate.
The operator runs only 1M sessions.

Demand does not move with the window — only the budget does — so the
existing measurements rescore with no re-probing needed:

| Session | Demand | vs 40,000 (1M) | vs 8,000 (200k) |
|---|---|---|---|
| ordinary | 16,535 | **41%** | 2.07× |
| `viral` | 20,220 | **51%** | 2.53× |
| `dojo` | 23,287 | **58%** | 2.91× |

Corroborated by the 1M capture already in the fixtures, which emits no
budget warning at all.

Deployability now turns on whether a pair is **declared in use**.
Established-but-undeclared pairs are measured and reported, never gating
— so a session that does land at 200k is told it is non-conformant
rather than discovering bare skill names on its own. Declaring a pair is
a maintainer edit, so the gate cannot widen because someone selected a
different model.

## What this does to the argument

**Nothing the operator runs is in breach except `viral` at 95% on
Codex.**

That is the third consecutive revision where better information
*reduced* urgency: revision 7 found the measurements 1.78× overstated,
revision 10 found two of three Codex breaches were ordinary defects,
this one finds the remaining harness conformant in practice. The spec
says so plainly rather than preserving a threat that no longer exists.

What survives is now the whole case, and it is governance rather than
firefighting:

- The Claude Code margin is **one model selection wide** — 58% or 291%,
same catalog, same day, no announcement either way
- The figures have moved 2× in a day, twice, with no distribution
decision taken
- Cross-machine agreement broke silently within 24 hours of being
verified
- The curated 31-skill set is installed state, not a reviewable
declaration

Calibration recorded for later: at 200k, bundled entries are **exempt
from stripping** and take 3,774 of the 8,000 budget at full length,
leaving ~4,226 against a mean 313 chars/entry — about **13 dojo
skills**, which is `core` plus roughly one overlay, matching what
SC-03's fits-proof already specifies.

## Plan changes from Task 0

- **Task 3's detector: three shapes → five.** Task 0's read of
`render.rs` found Codex's third tier (whole-skill omission, which *does*
warn in-prompt) and that its two truncations are different mechanisms —
the 1,024-char pre-cap appends `"..."`, budget clipping appends nothing.
- **Task 3 must import `probe_codex`'s primitives**, not write second
implementations. Two are not what a reasonable reading of the source
suggests, and Task 0 got both wrong first.
- **Task 3's fit proof: two declared pairs** plus a reported-not-gated
case for 200k. Policy files carry explicit `deployable: true|false`.
- **Task 10 rescoped.** It existed to adjudicate the 56%/85% vs 50%/75%
conflict — already resolved by two retractions, so adjudicating now
would re-litigate withdrawn measurements. It emits the first
machine-generated position instead.

## Verification

- `validate_spec.py` PASS, `validate_plan.py` PASS
- `pytest tests/ -q` → 350 passed
- No success criterion beyond SC-03, no evaluation scenario, and no
authority boundary changed

🤖 Generated with [Claude Code](https://claude.com/claude-code)
davisbuilds added a commit that referenced this pull request Aug 3, 2026
)

Follows Task 0 (#56) and spec revision 11 (#57). Suite **350 → 391**.

## What landed

Eight profile definitions (one file each, so a duplicate `name` is a
detectable condition rather than a silently last-wins key),
`harness-equivalences.yaml`, `scripts/profiles/definitions.py`, and
`profiles/README.md`.

Overlays are deliberately **3–4 members**, not categories. `core` (8)
plus any one overlay lands at 11–12, inside the ~13-skill figure a 200k
Claude Code window allows. An overlay of nine would not be deployable at
all.

| Overlay | Members (⚓ = SC-02 anchor) |
|---|---|
| engineering | ⚓`create-cli` ⚓`secure-code` `api-design`
`repo-hardening` |
| research | ⚓`deep-research` ⚓`research-architect` `fetchmd` |
| design | ⚓`design-critique` ⚓`web-design-guidelines` `frontend-design`
|
| knowledge | ⚓`obsidian-markdown` ⚓`session-retro` `compound-docs` |
| shipping | ⚓`gh-commit-push-pr` ⚓`vercel-deploy` `vercel-preview-logs`
|
| skill-authoring | ⚓`skill-creator` ⚓`skill-standardizer` `skill-evals`
|

Each non-anchor member is a *sibling of an anchor along the axis the
anchor sets* — `api-design` is `create-cli` at the service boundary,
`repo-hardening` is `secure-code` at repo scope, `frontend-design` is
the only producer among two auditors, `vercel-preview-logs` is the only
skill that reads back what a deployment did. **21 of 48 catalog skills
belong to no overlay**, which is correct rather than a gap. Every member
carries an inline reason; every file records what was deliberately
excluded.

## The mutation probe found three holes

All 27 validation rules were probed by breaking each and confirming a
test fails. Three did not:

- **`members: core`** — plausible YAML, neither the sentinel nor a list,
and it would have been iterated character by character into four
one-letter "members".
- An unrecognised key was silently accepted.
- **`equivalence_identity` without its `sorted()` passed everything**,
because every test reached identity through the loader, which sorts
first. Order-independence was being provided by the wrong function and
the guarantee was untested.

All three closed. That third one is the same shape as Task 0's `sent :=
loaded` hole — a property tested only through a caller that already
satisfies it.

Also built in, and worth stating because it is invisible otherwise:
**the non-`core` member count is checked before the anchors.** Every
anchor is itself a non-`core` skill, so an overlay holding its anchors
can never fail the count — with the checks reversed, that rule could not
fire on any input.

## Review changes

**Withdrew the `gpt-imagen` ↔ Codex `imagegen` declaration.** It was the
only entry resting on a capability judgement across *differing* names,
and the judgement is unverified: dojo's pins `gpt-image-2` and exposes
inpainting, masking, and batch output, none of which the bundled
description claims. The asymmetry decides it — a wrong declaration
silently removes a selected skill, an undeclared collision is *reported*
— and it buys nothing today since `gpt-imagen` is in no overlay. Kept as
a candidate with the comparison it needs.

Two declarations remain, both same-name same-capability:
`skill-creator`, `skill-installer`.

## Three things Task 1 falsified in its own inputs

1. **`review-agent` is not a Codex-listed entry.** It exists at
`~/.codex/skills/.system/review-agent/` and appears in neither the Task
0 capture nor a live re-probe. Spec and plan both named it as bundled.
Only listed entries can displace a member — this is the
filesystem-is-not-the-listing rule catching this contract's own prose.
2. **The plan's equivalence seed contradicted its own rule.** It named
six Codex skills to seed from, while the next sentence forbids declaring
a pair whose dojo member does not exist — and three of the six have no
dojo counterpart. Rewritten so the rule governs, with "observed in a
listing" made an explicit second condition.
3. **"8 rejection cases" was stale** (27 implemented), and `__init__.py`
was listed as `Create` though it shipped in Task 0.

## Verification

- `pytest tests/ -q` → **391 passed**
- CI-shaped run (`cd /tmp`, `HOME=/tmp/fake-home`, `PATH=/usr/bin:/bin`)
→ identical
- `validate_spec.py` PASS, `validate_plan.py` PASS
- Every rejection test asserts the error *message* names the offending
profile and member; no test compares against a hardcoded catalog total —
`full` resolves against `len(skills.json)` computed at test time

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant