mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-09 10:18:32 +01:00
GST-1088 lane C after #3909: adversarial-pass contract marker (optional agent_dispatch marker in the contract resolver), cap comments cite gate rev 2, pins at the combined measurement
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
f092f83df0
commit
27052ffd77
7 files changed
+28
-16
No files matched your search
@@ -18,7 +18,7 @@ export type ContractItem =
|
||||
| { id: string; kind: 'report_labels'; heading: string; labels: string[] }
|
||||
| { id: string; kind: 'check_after_last_edit' }
|
||||
| { id: string; kind: 'selected_specialists_dispatched'; marker: string }
|
||||
| { id: string; kind: 'agent_dispatch'; match: string };
|
||||
| { id: string; kind: 'agent_dispatch'; match: string; marker?: string };
|
||||
|
||||
export interface SkillContract {
|
||||
version: 1;
|
||||
@@ -32,6 +32,11 @@ const KIND_FIELDS: Record<ContractItem['kind'], string[]> = {
|
||||
agent_dispatch: ['match'],
|
||||
};
|
||||
|
||||
// Fields a kind may carry; when present each must be non-empty text. bin/gstack-skill-contract checks agent_dispatch's marker before its match.
|
||||
const OPTIONAL_FIELDS: Partial<Record<ContractItem['kind'], string[]>> = {
|
||||
agent_dispatch: ['marker'],
|
||||
};
|
||||
|
||||
// The resolver and section that render the step each kind checks today. A host that suppresses the resolver, or a loadout that excludes the section, never shows that step, so its items are left out of the list there.
|
||||
const BACKED_BY: Partial<Record<ContractItem['kind'], { resolver: string; section: string }>> = {
|
||||
selected_specialists_dispatched: { resolver: 'REVIEW_ARMY', section: 'review-army' },
|
||||
@@ -69,11 +74,14 @@ export function parseSkillContract(value: unknown, where: string): SkillContract
|
||||
if (seen.has(raw.id)) fail(where, at + ' duplicate id "' + raw.id + '"');
|
||||
seen.add(raw.id);
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (key !== 'id' && key !== 'kind' && !KIND_FIELDS[kind].includes(key)) fail(where, at + ' (' + kind + ') has unknown key "' + key + '"');
|
||||
if (key !== 'id' && key !== 'kind' && !KIND_FIELDS[kind].includes(key) && !OPTIONAL_FIELDS[kind]?.includes(key)) fail(where, at + ' (' + kind + ') has unknown key "' + key + '"');
|
||||
}
|
||||
for (const field of KIND_FIELDS[kind]) {
|
||||
if (field !== 'labels' && !isText(raw[field])) fail(where, at + ' ' + field + ' must be a non-empty string');
|
||||
}
|
||||
for (const field of OPTIONAL_FIELDS[kind] ?? []) {
|
||||
if (field in raw && !isText(raw[field])) fail(where, at + ' ' + field + ' must be a non-empty string');
|
||||
}
|
||||
if (kind === 'report_labels') {
|
||||
if (!Array.isArray(raw.labels) || raw.labels.length === 0) fail(where, at + ' labels must be a non-empty list');
|
||||
for (const label of raw.labels as unknown[]) {
|
||||
|
||||
Reference in new issue
Block a user