Add a skill-shipped grounding gate and the spike findings - #44
Draft
baonguyenNava wants to merge 3 commits into
Draft
Add a skill-shipped grounding gate and the spike findings#44baonguyenNava wants to merge 3 commits into
baonguyenNava wants to merge 3 commits into
Conversation
Issue #30 deferred the Slack bot itself, so the transport from a Slack channel to the deployed QA Lambda was never designed. Two facts force the shape: Slack's 3-second ACK deadline against a 9-90s answer, and the RESERVED_CONCURRENCY=3 spend ceiling that a shared ACK/work pool would collapse under load. Hence a thin public gateway in front of the existing IAM-only function, both from one image. Records the reasoning so the implementing PR argues about code, not architecture. Also updates the .dockerignore comment that claimed nothing under docs/superpowers was in git — this commit makes that false, and the exclusion is now a real one rather than insurance. Relates to #30 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spike #42 asks whether a Claude Tag skill can preserve the guarantee strata-qa enforces without deploying the Slack gateway #40 designs. The gate is the part that has to survive the move, so it is the part this ports: the model proposes citations, and code decides whether they verify. The port is dependency free and runs on Node's built-in test runner, so the skill adds nothing to this repository's dependency surface. It sits under bin/ rather than workflows/ because the .mjs files under skills/generate-strata-docs/workflows/ are Workflow tool orchestration scripts that use ambient agent() and phase() globals and cannot be run with node. Two kinds of artifact, two directories. A port claim is worth nothing unverified, so differential.test.mjs runs generated cases through both implementations and compares the whole GroundingResult. Its own detection power was checked by injecting the per-quote versus per-document error the original's comments warn about, which it caught in 53 of 500 cases. Candidate selection scores graph.json rather than walking the filesystem, which excludes .verification, .curation, and superpowers by data structure instead of by the .dockerignore rules the image relies on. It can only cost recall, never soundness, because the gate rechecks every quote regardless of what selection handed the model. Relates to #42 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C81WXLzPWLPryrEPsjZ6BC
The port is faithful. Twenty thousand generated cases across four seeds produced zero disagreements with the original, on a harness whose detection power was demonstrated rather than assumed. Corpus scoping comes out stronger than the Lambda's, and the deployment surface, the Cursor dependency, and corpus staleness all disappear with it. What does not survive is the guarantee that the check runs at all. In the Lambda, run.ts calls ground() unconditionally, so trusted code wraps the untrusted model. In a skill the agent decides. That inversion is not closable by code, only observable, which turns the remaining question into a product judgment rather than an engineering one. Amends the 2026-07-27 design spec status so an approved spec is not silently orphaned while that judgment is pending. Relates to #42 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C81WXLzPWLPryrEPsjZ6BC
baonguyenNava
changed the base branch from
baonguyenNava/40-strata-qa-slack-bot
to
baonguyenNava/30-strata-qa-cli
July 28, 2026 22:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Delivers the spike in #42: a skill that answers Strata documentation questions with every quote verified against its cited source, plus the findings on whether it can replace the Lambda transport #40 designs.
The port is faithful and the deployment surface disappears, but the guarantee that the check actually runs does not survive and cannot be recovered by code. That tradeoff is the finding, and it turns the remaining question into a product judgment rather than an engineering one.
Relates to #42
How
Three stages, two of them deterministic programs rather than model judgment:
verify-answer.mjsis a dependency-free port ofstrata-qa/src/grounding.ts. The model proposes citations; the script checks each quote appears verbatim in the file it cites and demotes the verdict when it does not. The answer text is released only onanswered, exactly asrun.tsdoes it.select-candidates.mjsscoresdocs/graph.jsoninstead of walking the filesystem. Because the graph indexes only the 56 documents underdocs/sources/,.verification,.curation, andsuperpowersare excluded by data structure rather than by the.dockerignorerules the image depends on. Selection can only cost recall, never soundness, since the gate rechecks every quote regardless.Scripts live under
bin/rather thanworkflows/on purpose. The.mjsfiles underskills/generate-strata-docs/workflows/areWorkflowtool orchestration scripts using ambientagent()andphase()globals, and cannot be run withnode. Two kinds of artifact, two directories.Nothing under
strata-qa/changed, and no dependency was added to the repository.Test plan
node --test "skills/answer-strata-question/bin/*.test.mjs": 33 pass, 0 fail. That is 32 inverify-answer.test.mjs(all 20 cases ported fromgrounding.test.ts, plus 12 for the CLI surface, including assertions thatlow_confidenceandno_matchnever leak the proposed answer), and a 33rd fromdifferential.test.mjs, whose single test is the differential sweep described in the next bullet (500 cases per run at its defaults, widened for the numbers below).ground(): 20,000 generated cases across seeds 20260728, 1, 99991, and 424242, with zero disagreements on the fullGroundingResult. Reproduce one seed at a time withCASES=5000 SEED=1 node --test skills/answer-strata-question/bin/differential.test.mjs, which is 5,000 of the 20,000; the full run is that command once per seed.distinctDocs < docsCited) with per-quote gating (quotesVerified < citationsTotal) was caught in 53 of 500 cases and by one unit test. Reverted, and both suites returned to green.answeredwith a verdict line and exit 0; changing one character of that quote returnsno_matchwith the answer withheld and exit 2; adding an unverifiable second document returnslow_confidenceand exit 1; prose with no JSON exits 6.--limit 8, both means across the nine, which is the table in the findings spec. "What is the best pizza topping?" scores zero and is refused before any document is read.python -m pytest65 passed,MANIFEST_OK/DOCS_OK/GRAPH_OKwith no graph diff, andstrata-qastill at 213 passing tests.Notes for reviewers
This is stacked behind #38 and #41. Base is
baonguyenNava/30-strata-qa-cli, the same base #41 targets, so the diff carries #41's commit as well: the 2026-07-27 design spec and the.dockerignorecomment change that came with it. Review those in #41; what is new here isskills/answer-strata-question/and the findings spec. The findings commit amends the 2026-07-27 spec's status line, which is why both PRs touch that file. Merge order is #38, then #41, then this, and the overlap drops out of this diff once #41 lands.#42 should stay open. Its Output section asks for a decision logged on the issue, and that decision is yours, not this PR's. The findings deliberately stop at presenting the tradeoff.
The limitation we cannot close. Control is inverted, and no amount of code un-inverts it. In the Lambda, trusted code wraps the untrusted model; in a skill, the agent calls the trusted code. Neither auto mode allow rules (which permit rather than compel) nor repository hooks (which cannot gate the Slack post, since posting is the harness's output channel) fix that. The mitigation is that the gate writes the final message and stamps a verdict line, so a skipped gate is visible rather than prevented. Prevention became detection, and that is a downgrade worth naming.
Deliberate duplication.
ground()now exists twice. Deduplicating was out of scope for a spike, and the differential harness keeps it safe by failing the moment the two diverge. The findings recommend making the.mjsthe source of truth if this direction is adopted, and deleting the skill outright if it is not.Unmeasured. Whether Claude Tag reliably invokes the gate, end-to-end answer quality, real cost, and whether the eight-candidate cap costs recall all need a live workspace and a Team or Enterprise entitlement. Confirming that entitlement is the cheapest next step and blocks the rest.
Small wrinkle worth keeping if this gets CI wiring. Use the glob, not the bare directory:
node --test <dir>also executesverify-answer.mjs, which reads empty stdin and exits non-zero.