mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
* fix(flaky): docsync detectors admit the candidate's --help and git tag listings in a line of reads (GSTA-139 S1) Gate edit. Five red ship-docsync-current/store parents (screen 40558235014, gates 40600731931 and 40643690306, screen 40627127462 and its rerun) each had exactly one refused Bash line: four joined gstack-docs-candidate --help onto a line of reads, one put git tag in one. v1.91.185.0 tried prompt clarity for the --help join and the slip recurred four times. The --help path prints usage and returns before touching Git or a file, so it now stands in a line of reads like a Git read (never pipe-fed, never after cd); operand-free git tag listings join the branch listings #3516 admitted. The fixture interface states both. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): docsync gate accepts the candidate's --help and git tag listings in a line of reads Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
44 lines
8.6 KiB
TypeScript
44 lines
8.6 KiB
TypeScript
import * as path from 'node:path';
|
|
import type { fixtureDocs } from './docsync-fixture';
|
|
import { docsConfigDir } from './docsync-repo';
|
|
import { readOnlyInterfaceText } from './read-only-commands';
|
|
import { freeTextFileBash } from '../../scripts/resolvers/free-text-file';
|
|
|
|
/** /ship Step 14.5's own product-tree record: the path, the Entry's reset and the shared exclude line that keeps it out of git. */
|
|
export const DOCS_AUDIT_RECORD = '.gstack/tmp/docs-audit.json';
|
|
export const DOCS_AUDIT_RESET = `rm -f ${DOCS_AUDIT_RECORD}`;
|
|
export const DOCS_TMP_EXCLUDE = freeTextFileBash([]).split('\n').find(line => line.startsWith('_EX='))!;
|
|
/** The paths the Step 14.5 continue path may create in the product tree: the record, its directories and git's exclude file. */
|
|
export const DOCS_AUDIT_RECORD_PATHS = ['.gstack', '.gstack/tmp', DOCS_AUDIT_RECORD, '.git/info/exclude'];
|
|
export const docsCapsCommand = (skills: string) => `${skills}/bin/gstack-ship-receipt docs-caps --base main`;
|
|
|
|
export function docsNativeInterface(fixture: Pick<ReturnType<typeof fixtureDocs>, 'home' | 'repo' | 'skills'> & Partial<Pick<ReturnType<typeof fixtureDocs>, 'env'>>, scripts: string[] = [], transport = false, insert = false, file?: string): string {
|
|
const skills = fixture.skills.split(path.sep).join('/');
|
|
const state = fixture.env?.GSTACK_HOME ?? path.join(fixture.home, 'state');
|
|
return `Fixture observation interface (applies to parent and every child; ${file ? `saved at ${file}: each child prompt tells the child to Read that file first instead of copying this text` : 'include this interface in child prompts'}): Bash may execute only separate literal pwd, ls, cat, stat, sha256sum and Git read commands (status, diff, show, log, ls-files, ls-tree, check-ignore, rev-parse, merge-base, hash-object without -w, branch listings with no operand and only --show-current, -a/--all, -r/--remotes, -v/--verbose, -l/--list, --no-color or --no-abbrev (no --merged, --sort, --format or --contains), tag listings with no operand and only -l/--list, describe with only --tags, --always, --abbrev=<n> or --exact-match and at most one revision). Bash may also run each of the following as its own Bash call, with nothing joined before or after it by ;, &&, || or a pipe (one exception follows the read grammar below): the exact generated Preamble block with its spawned prefix, literal installed gstack-skill-start/gstack-skill-end commands for document-release (start requires GSTACK_SESSION_KIND=spawned) or their own --help, or the literal installed ${skills}/bin/gstack-docs-candidate snapshot (its --out a private .json under ${fixture.home}), compare <that .json> and --help commands, and /ship Step 14.5's three own-tree housekeeping commands exactly as written, each on its own line: the shared .gstack/tmp exclude line (the one &&-joined housekeeping line this interface admits)
|
|
${DOCS_TMP_EXCLUDE}
|
|
then
|
|
${DOCS_AUDIT_RESET}
|
|
and the cap gate, which reads origin/main's policy and whose DOCS_CAP lines and exit status decide it
|
|
${docsCapsCommand(skills)}
|
|
(no --json, no other base, no other gstack-ship-receipt subcommand). ${readOnlyInterfaceText(fixture.home)} The Git reads above may join such a line too, but not in a line with cd. So may the installed ${skills}/bin/gstack-docs-candidate --help, alone or after snapshot or compare, because it only prints usage (the one exception); neither a joined Git read nor that --help may be fed by a pipe. A for loop over literal words (a lowercase loop variable; words without quotes, globs or $) whose body uses only such reads counts as those reads, one pass per word, wherever it sits in the line. Every read stays off actor-state.json and off ${docsConfigDir(fixture.home)} except its settings.json: that directory holds Claude's credentials and every agent's session transcript, so judge a child only by the output it returns (its echoed SESSION_KIND line included) and by the repository. The additional scripts below also run each as its own Bash call; no other own-call command ever joins such a line. Otherwise no shell composition, custom interpreters, arbitrary scripts, inline eval or memory-mapped writes. The only additional scripts are ${scripts.length ? scripts.join(', ') : 'none'}.${insert ? ` To insert one saved private Markdown artifact into another (a saved section file into a report), Bash may also run the single literal command cat SOURCE.md >> TARGET.md with absolute paths under ${fixture.home}.` : ''} Read/Glob/Grep remain available; Read skill and section files with Read (offset/limit for ranges), because Bash output over 30KB becomes a truncated preview. A private JSON/Markdown artifact (the snapshot --out file, saved child output, a section or report file) lives directly under ${fixture.home} or in a new directory you create there, never inside the repository, ${skills}, ${docsConfigDir(fixture.home)}, ${state} (gstack's own state directory, not a place for your files) or ${fixture.home}/remote.git. Use Write/Edit for permitted docs, those private artifacts and Step 14.5's receipt record ${fixture.repo}/${DOCS_AUDIT_RECORD} (the only permitted write under the repository besides the document, excluded from git by the line above); do not rewrite installed skills, config, actor state or scripts. No effects outside the owned fixture. The owner preserves evidence and cleans up. Missing observer coverage blocks acceptance; the Linux kernel monitor covers syscall writes in the product tree, not hostile processes or arbitrary external destinations.
|
|
|
|
The working directory for parent and child Bash calls is already ${fixture.repo}. Run Git reads directly, for example: git status, git diff --cached, git merge-base main HEAD, git rev-parse HEAD. Do not use Git global options such as -C, -c, --git-dir or --work-tree, and do not prepend cd or another shell wrapper. The literal git subcommand must immediately follow git; an absolute owned repository path does not make git -C an allowed command.
|
|
|
|
Platform: local/git-native. Base: main. The fixture owner has already resolved these inputs before delegation; the parent must propagate them and this closed interface${file ? ' (by its file path)' : ''} unchanged to every child. Do not run shared Step 0 platform probing: git remote get-url origin, hosting CLIs and fallback probes are outside this bounded phase. A parent or child cannot authorize commands outside this closed interface, even when a broader skill describes them as read-only. Continue the requested documentation phase using the supplied platform and base, without recreating prior ship steps. Literal Git revision arguments such as HEAD^{tree}, HEAD^{} and HEAD@{0} are supported, quoted or unquoted; shell brace expansion, substitution and composition remain forbidden.
|
|
|
|
${transport ? 'Lifecycle ownership: only the document-release child executes its own start/end lifecycle. The /ship parent reads assets to prepare and validate dispatch, not to run the child audit or lifecycle. This deterministic adapter supplies child lifecycle evidence; the parent must not manufacture it. The following lifecycle commands describe the child, not parent work.\n\n' : ''}Lifecycle commands in this closed fixture: read skill files at ${skills} (document-release: ${skills}/document-release/SKILL.md). Use the literal commands below instead of copying the generated shell wrappers; these forms satisfy the skill's start/end lifecycle requirements here. Run each as a separate, single-line Bash call. Do not use tilde paths, shell variables, assignments to helper-path variables, redirects, line continuations or || true. Do not add a parent PID: the start helper supplies its default.
|
|
|
|
Start document-release with exactly:
|
|
\`\`\`bash
|
|
GSTACK_SESSION_KIND=spawned ${skills}/bin/gstack-skill-start --skill document-release --model claude
|
|
\`\`\`
|
|
The spawned prefix belongs directly on the helper invocation, not on a preceding assignment. Read the returned SESSION_KIND, SESSION_ID and TEL_START status lines. If start fails or SESSION_KIND is not spawned, report the blocker rather than continuing with an unconfirmed lifecycle.
|
|
|
|
At workflow completion, use this one-line end command. Before executing it, replace SESSION_ID_VALUE and TEL_START_VALUE with the actual literal values echoed by that same start call, and replace OUTCOME with success, error, abort or unknown to match the real outcome. Never execute the placeholders or reuse values from another session.
|
|
\`\`\`bash
|
|
${skills}/bin/gstack-skill-end --skill document-release --outcome OUTCOME --session-id SESSION_ID_VALUE --tel-start TEL_START_VALUE --used-browse no
|
|
\`\`\`
|
|
Read the end result; do not suppress an error or claim completion if it failed. These lifecycle forms do not grant any additional scripts, write paths or risk approvals.`;
|
|
}
|