Skip to content

fix(prompt-surface): let the generator own the A1 golden's hash baseline - #292

Merged
alfonso-magic-context merged 2 commits into
cortexkit:masterfrom
iceteaSA:fix/export-agent-surface-section3
Aug 10, 2026
Merged

fix(prompt-surface): let the generator own the A1 golden's hash baseline#292
alfonso-magic-context merged 2 commits into
cortexkit:masterfrom
iceteaSA:fix/export-agent-surface-section3

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Closes #290. Both items you asked for, in one commit off clean master @ 77c51da9.

1. The generator owns section 3

export-agent-surface.ts emitted only sections 1 and 2, so the hash baseline table was hand-maintained and regenerating the golden deleted it.

The table is now derived rather than transcribed: Buffer.byteLength(text, "utf8") and createHash("md5").update(text) over the same variants array section 1 already walks.

Verified by regenerating over the committed golden — output is byte-identical apart from the date stamp, all four hash rows included. One detail worth flagging: the committed prose contains output.system.join("\\n") (escaped backslash-n, not a newline). My first pass emitted \n and the diff caught it; the emitted string now reproduces the committed bytes exactly. The golden itself is untouched in this PR — the fix is the generator, and a date-only churn would just be noise.

2. Both slice call sites now throw

tool-registry.test.ts and pi-plugin/src/tools/index.test.ts both did:

document.slice(indexOf("## 2. Tool surface"), indexOf("## 3. System-prompt hash baseline"))

-1 for a missing heading, so the slice returned "", the golden parsed as zero tools, and the tests passed comparing empty to empty. Agreed this is the half that hides the next regression. Both resolve offsets through a sectionOffset() helper that throws a named error.

Red-check

With section 3 stripped from the golden:

error: A1 golden is missing the "## 3. System-prompt hash baseline" section heading
(fail) createToolRegistry — prompt-surface registration > matches the A1 golden …
(fail) registerMagicContextTools — prompt-surface registration > matches the A1 golden …

Same mutation on master: both green. That gap is the bug, and it is now closed on both hosts.

Gates

plugin 3609 pass / 3 fail †
pi-plugin 740 / 0
cli 296 (2 skip)
typecheck 0 across 3 packages
lint clean

† Pre-existing @opentui TDZ errors in tui-compiled-runtime-imports.test.ts (Cannot access 'TreeSitterClient' before initialization). Unrelated to this change — it touches no TUI file — and reproducible on a clean upstream/master worktree with a fresh install. Your CI is green on master, so this looks local to my environment rather than something you need to act on; mentioning it only so the count is not mistaken for a regression here.

Files: export-agent-surface.ts, tool-registry.test.ts, pi-plugin/src/tools/index.test.ts.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Owns the A1 golden’s “System-prompt hash baseline” in the generator and makes tests fail when the section is missing. Prevents silent golden truncation and false green runs; also centralizes golden accessors to remove duplicated parsing and path math.

  • Bug Fixes

    • packages/plugin/scripts/export-agent-surface.ts now emits section 3 by deriving the table from the guidance text (bytes via Buffer.byteLength(..., "utf8"), MD5 via createHash("md5")). Output matches the current golden (date-only diff).
    • packages/plugin/src/plugin/tool-registry.test.ts and packages/pi-plugin/src/tools/index.test.ts now throw when required section headings are missing, replacing the silent slice(...) behavior that produced empty tool sets.
  • Refactors

    • Added packages/plugin/src/shared/prompt-surface-a1-golden.ts exporting A1_TOOL_SECTION_HEADING, A1_HASH_BASELINE_HEADING, readA1GoldenDocument, and a1GoldenSectionOffset. Both test suites now import these (Pi via @magic-context/core/shared/prompt-surface-a1-golden) to dedupe logic and ensure consistent guards.

Written for commit ceb4cb0. Summary will update on new commits.

Review in cubic

Greptile Summary

The PR makes the prompt-surface generator produce the system-prompt hash baseline and prevents malformed golden sections from silently yielding empty comparisons.

  • Derives guidance byte counts and MD5 hashes from the generator’s existing variants.
  • Centralizes golden-file loading, section headings, and missing-heading validation.
  • Updates both plugin hosts’ tests to use the guarded shared helpers.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/plugin/scripts/export-agent-surface.ts Generates section 3 from the same guidance variants used by section 1, including UTF-8 byte counts and MD5 hashes.
packages/plugin/src/shared/prompt-surface-a1-golden.ts Adds shared golden-file access and explicit errors for missing required section headings.
packages/plugin/src/plugin/tool-registry.test.ts Replaces unchecked heading offsets and local file-path handling with the guarded shared helpers.
packages/pi-plugin/src/tools/index.test.ts Uses the shared golden reader and guarded section offsets while retaining the repository’s established cross-package import pattern.
packages/plugin/src/shared/prompt-surface-a1-golden.md Contains the generated A1 prompt-surface snapshot consumed by both test hosts.

Reviews (2): Last reviewed commit: "refactor(prompt-surface): extract the A1..." | Re-trigger Greptile

Closes cortexkit#290.

prompt-surface-a1-golden.md says it is generated by export-agent-surface.ts, but
the script emitted only sections 1 and 2. Section 3 (the system-prompt hash
baseline) was hand-maintained, so running the script the way its own usage line
documents deleted it -- exit 0, no warning -- and system-prompt-hash.test.ts then
threw "Malformed A1 primary guidance golden".

The generator now emits section 3. The table is DERIVED, not transcribed: the
hash handler persists the MD5 of output.system.join("\n"), and in the
no-host-prefix baseline each guidance section IS the whole system array, so the
hash is just the MD5 of the guidance bytes already emitted in section 1. Verified
by regenerating over the committed golden -- the output is byte-identical apart
from the date stamp, including all four hash rows.

Also hardens the two consumers that slice on that heading (tool-registry.test.ts
and pi-plugin/src/tools/index.test.ts). Both did

    document.slice(indexOf("## 2. Tool surface"), indexOf("## 3. ..."))

which yields -1 for a missing heading, so the slice silently returned "" and the
golden parsed as ZERO tools -- and the tests still PASSED, comparing empty to
empty. That was the worse half of the trap: one loud error while every other
reader quietly degraded. Both now resolve offsets through a sectionOffset()
helper that throws a named error.

Red-checked: with section 3 stripped from the golden, both call sites now fail
with `A1 golden is missing the "## 3. System-prompt hash baseline" section
heading`. On master the same mutation leaves them green.

Gates: plugin 3609/3, pi 740/0, cli 296 (2 skip), typecheck 0 x3, lint clean. The
3 plugin failures are pre-existing @OpenTui TDZ errors in
tui-compiled-runtime-imports.test.ts, unrelated to this change (it touches no TUI
file) and reproducible on a clean upstream/master worktree.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/plugin/src/plugin/tool-registry.test.ts Outdated
…ed module

Addresses cubic's P3 on cortexkit#292: the sectionOffset() guard was duplicated verbatim
across tool-registry.test.ts and pi-plugin/src/tools/index.test.ts, both parsing
the same golden.

New src/shared/prompt-surface-a1-golden.ts owns three things that were previously
copy-pasted per package: the two section-heading strings, the document read, and
the offset guard. Pi already consumes plugin/src through the
@magic-context/core/* tsconfig alias, so this is the established seam rather than
a new dependency.

The module resolves the golden relative to ITSELF, which removes the per-package
`../..` arithmetic (`../shared/...` on OpenCode, `../../../plugin/src/shared/...`
on Pi). That arithmetic was the more dangerous half of the duplication: it is
invisible to the type checker and rots silently when files move.

Scope note: the two readA1PrimaryGuidance() readers are deliberately left alone.
They regex the whole document rather than slicing between headings, so they were
never exposed to the -1 bug this PR fixes, and folding them in would widen an
unrelated PR. The heading constants are exported and ready if a later change
wants them.

Re-verified the red-check through the shared helper -- with section 3 stripped,
both consumers still fail with the named error:

  A1 golden is missing the "## 3. System-prompt hash baseline" section heading

Gates: plugin 3609/3, pi 740/0, cli 296 (2 skip), typecheck 0 x3, lint clean.
Same 3 pre-existing @OpenTui TDZ failures as the parent commit; this touches no
TUI file.
@iceteaSA

iceteaSA commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai Valid — fixed in ceb4cb01.

New packages/plugin/src/shared/prompt-surface-a1-golden.ts owns the two heading constants, the document read, and the offset guard. Pi already consumes plugin/src through the @magic-context/core/* tsconfig alias, so this uses the established seam rather than introducing a new dependency.

One thing your report understated, and it is the better half of the fix: the duplicated path arithmetic was the more fragile part. The two readers resolved the same file as ../shared/… and ../../../plugin/src/shared/… respectively — invisible to the type checker, silently wrong the moment either file moves. The shared module resolves the golden relative to itself, so neither consumer carries a path any more.

Deliberately left out of scope: the two readA1PrimaryGuidance() readers in system-prompt-hash.test.ts and pi-plugin/src/system-prompt.test.ts. They regex the whole document instead of slicing between headings, so they were never exposed to the -1 bug this PR fixes; folding them in would widen the PR into unrelated territory. The heading constants are exported and ready if a later change wants them.

Re-ran the red-check through the shared helper — with section 3 stripped from the golden, both consumers still fail with the named error:

error: A1 golden is missing the "## 3. System-prompt hash baseline" section heading
(fail) createToolRegistry — prompt-surface registration > matches the A1 golden …
(fail) registerMagicContextTools — prompt-surface registration > matches the A1 golden …

Gates unchanged: plugin 3609/3, pi 740/0, cli 296, typecheck 0 ×3, lint clean. The 3 are the pre-existing @opentui TDZ failures noted in the PR description.

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

@cubic-dev-ai Valid — fixed in ceb4cb01.

New packages/plugin/src/shared/prompt-surface-a1-golden.ts owns the two heading constants, the document read, and the offset guard. Pi already consumes plugin/src through the @magic-context/core/* tsconfig alias, so this uses the established seam rather than introducing a new dependency.

One thing your report understated, and it is the better half of the fix: the duplicated path arithmetic was the more fragile part. The two readers resolved the same file as ../shared/… and ../../../plugin/src/shared/… respectively — invisible to the type checker, silently wrong the moment either file moves. The shared module resolves the golden relative to itself, so neither consumer carries a path any more.
...

@iceteaSA Right on both counts — the shared module is the stronger fix, and readA1PrimaryGuidance() was correctly left alone.

One thing worth a quick sanity check on your side, since your @magic-context/core/* alias crosses a package boundary: Bun's tsconfig paths resolution at workspace boundaries is a known weak spot (there are open Bun issues about it — e.g. oven-sh/bun#21056 and #12262). Your existing tests presumably already exercise this seam and pass, so it's likely fine in practice, but since readA1GoldenDocument() now leans entirely on import.meta.dir resolving beside the source .ts file, it's worth confirming Bun resolves it against the source tree rather than a dist output when run from the Pi package. If your green Pi run already proves it, no action needed — just flagging the one assumption the refactor quietly depends on, since the old per-package paths were the exact kind of thing that masked this.

Otherwise the scoping is exactly what I'd want: the heading constants exported for later consolidation, the red-check re-run through the shared helper, and no widening into the regex-based readers.

Tip: get faster answers by chatting with cubic’s review copilot in the review UI

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/plugin/src/shared/prompt-surface-a1-golden.ts">

<violation number="1" location="packages/plugin/src/shared/prompt-surface-a1-golden.ts:19">
P3: The `A1_TOOL_SECTION_HEADING` constant doesn't mirror the actual golden heading — the file declares `## 2. Tool surface (description + parameters as serialized to the provider)`, while the constant only carries the `## 2. Tool surface` prefix. It works only because `indexOf` does a prefix/substring match, which contradicts the module's own stated contract (that these heading strings make a golden format change a one-file edit). If the golden ever gains a section whose heading starts with `## 2. Tool surface`, the offset guard would silently resolve to the wrong section. Suggest storing the exact on-disk heading so the constant truly reflects the contract it documents.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* exactly the kind of thing that rots silently when files move.
*/

export const A1_TOOL_SECTION_HEADING = "## 2. Tool surface";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The A1_TOOL_SECTION_HEADING constant doesn't mirror the actual golden heading — the file declares ## 2. Tool surface (description + parameters as serialized to the provider), while the constant only carries the ## 2. Tool surface prefix. It works only because indexOf does a prefix/substring match, which contradicts the module's own stated contract (that these heading strings make a golden format change a one-file edit). If the golden ever gains a section whose heading starts with ## 2. Tool surface, the offset guard would silently resolve to the wrong section. Suggest storing the exact on-disk heading so the constant truly reflects the contract it documents.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/plugin/src/shared/prompt-surface-a1-golden.ts, line 19:

<comment>The `A1_TOOL_SECTION_HEADING` constant doesn't mirror the actual golden heading — the file declares `## 2. Tool surface (description + parameters as serialized to the provider)`, while the constant only carries the `## 2. Tool surface` prefix. It works only because `indexOf` does a prefix/substring match, which contradicts the module's own stated contract (that these heading strings make a golden format change a one-file edit). If the golden ever gains a section whose heading starts with `## 2. Tool surface`, the offset guard would silently resolve to the wrong section. Suggest storing the exact on-disk heading so the constant truly reflects the contract it documents.</comment>

<file context>
@@ -0,0 +1,41 @@
+ * exactly the kind of thing that rots silently when files move.
+ */
+
+export const A1_TOOL_SECTION_HEADING = "## 2. Tool surface";
+export const A1_HASH_BASELINE_HEADING = "## 3. System-prompt hash baseline";
+
</file context>

@alfonso-magic-context
alfonso-magic-context merged commit 639d80e into cortexkit:master Aug 10, 2026
14 checks passed
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 10, 2026
Third union since the fork-lane move; migrations.ts and storage-db.ts merged
with zero conflicts again.

  upstream   <= 75    byte-identical to upstream
  fork    10000-2     skill-memory P1 / P2 / P3a
  fork      10100     external-memory recall snapshot
  LATEST_SUPPORTED_VERSION = 75

Upstream merged our cortexkit#292 (639d80e), so the section-3 generator fix and the
shared prompt-surface-a1-golden.ts module are now upstream's. Both feature
branches dropped their local ports during rebase and took upstream's version;
skill-memory-pr re-applies only its two tool registrations on top.

Of the 11 conflicts, 8 had byte-identical parents versus the previous union and
were restored verbatim. STRUCTURE.md was the only genuine merge (3 TAKE-BOTH
regions: features overview, inject-compartments, and the per-file lists), because
upstream also added dreamer/provider-output-failure.ts to the same lists this
cycle -- so it is a three-way take, not two.

Budget fixture: kept the previous union's numbers (baseline 4193, ceiling 2096)
after confirming they still match this tree's measurement exactly. The staged
merge result had silently taken skill-memory-pr's 4087, which measures a tree
that does not exist -- neither parent's fixture is valid for the union, which is
why this file gets re-measured rather than picked every cycle.

Gates: plugin 3908/2, pi 745/0, cli 298 (2 skip), typecheck 0 x3, lint clean,
budget check green, fork-lane coexistence 25/0, tui-compiled regenerated, marker
sweep clean, externalRecallHash still 0x inside mustMaterialize. The 2 plugin
failures are upstream's own @OpenTui TDZ errors.
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.

export-agent-surface.ts omits section 3, so regenerating the A1 golden breaks the guidance-epoch test

2 participants