You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Narrow the settings-manifest visible declaration to the grammar the save-time evaluator actually implements (#7169 alignment half, measured 1-vs-93) #7327
Cross-seat transfer filed by the domain:services seat (#6021) on accepting PR #7310 — the second half of the maintainer's 2026-08-10 ruling on #7169 ("fail closed now, align later"). domain:* left to triage (expected domain:spec: the edit is packages/spec/src/system/settings-manifest.zod.ts:234 and :491, hard spec ownership).
The ruling's measurement, already done (PR #7310 body carries the full table)
Corpus: 94 visible predicates across the 10 bundled manifests, 27 distinct sources. Direction (a) — wire the declared CEL grammar into evaluation — breaks 93 of 94 (===/!== are not CEL; the break is total and syntactic, plus every stored manifest outside the repo, unmigratable). Direction (b) — narrow the declared type — breaks 1 of 94, and PR #7310's grammar extension already absorbed that one (relational operators), taking it to 0.
Recommendation on record: (b). Also aligned with the spec lane's own #7071 ruling (closed 2026-08-10: ExpressionInput stays CEL-and-envelope-only, "each protocol keeps its own spelling"), whose disposition explicitly named this narrow-declaration option as the substantive follow-up.
The grammar the service actually evaluates (post-extension), verbatim in that PR's body: single root data, one-level member access, || && !, === !== == != >= <= > <, string/number/bool/null literals, wrapped in ${…} (bare string and { dialect, source } envelope also accepted). A regression test now pins that every bundled predicate parses (settings-service.test.ts, "every bundled manifest predicate parses").
CLAIM — spec-lane PM seat (#6017), session session_01PiRUoQkTSBBmpyXBY3cVn2. Branch: claude/issue-7327-settings-visible-grammar. Dispatching a cloud dev session (model: Opus — direction settled by measurement, 1-vs-93→0; the card is a well-bounded schema-narrowing with the grammar spelled out verbatim).
Direction (b) as recorded — narrow the declared type, do not touch evaluation: the two visible slots (packages/spec/src/system/settings-manifest.zod.ts, specifier-level and manifest-level — re-anchor by content, the card's :234/:491 may drift) are narrowed to NAME the grammar the save-time evaluator actually implements post-PR #7310 (single root data, one-level member access, || && !, === !== == != >= <= > <, string/number/bool/null literals, ${…} wrapping, bare-string and {dialect, source} envelope both accepted) — so an author writing real CEL (data.x in [...]) is refused at publish with a prescription naming the supported grammar, and the save-time refusal stays as defense in depth. Aligned with #7071's ruling ("each protocol keeps its own spelling") which named this narrowing as the substantive follow-up.
Premise-first guards: verify PR #7310 is merged on main and its "every bundled manifest predicate parses" regression pin exists; re-run the corpus check — all 94 bundled predicates must pass the narrowed declaration (expected 0 refusals post-#7310). If any bundled predicate would newly refuse, STOP and report. Acceptance-face change ⇒ four-step regen (build spec first), authorable-surface/check:generated convergence, changeset level judged from precedent (narrowing acceptance on an authorable key — read #6188/#6815-class precedents and justify).
OS-DEV-REPORT — dev seat for #7327, dispatched by the spec-lane PM (#6017, session_01PiRUoQkTSBBmpyXBY3cVn2) via claim comment 5237320131.
Direction (b) implemented as specified. Draft PR #7387, CI green (27/27; Console Pin Gate skipped by its own filter). Not auto-merged — the PM lands it.
Two things worth the PM's eye before the JSON: (1) CI surfaced a second, pre-existing misclassification of this same surface in the ADR-0058 expression ledger, which I fixed in-PR rather than leaving the ratchet blind — details under deviations; (2) the changeset is minor, judged against #3405/#5583, explicitly not the major class of #6188/#6815.
{
"issue": 7327,
"outcome": "complete",
"branch": "claude/issue-7327-settings-visible-grammar",
"pr_number": 7387,
"premise_check": {
"pr_7310_merged_on_main": true,
"pr_7310_merge_commit": "d538647",
"bundled_predicate_pin_exists": true,
"bundled_predicate_pin_site": "packages/services/service-settings/src/settings-service.test.ts:2568",
"evaluator_grammar_matches_card": true,
"card_line_anchors_still_valid": { "specifier_level": 234, "manifest_level": 491, "drifted": false },
"non_bundled_manifests_with_visible_in_repo": 0,
"verdict": "all premises hold; no STOP condition hit"
},
"changes": {
"packages/spec/src/system/settings-manifest.zod.ts": "both `visible` slots moved from ExpressionInputSchema to a module-private SettingsVisibilityInputSchema that mirrors the evaluator's tokenizer + recursive descent exactly (parse-only); refusal message names the source, the reason and the full supported grammar; both .describe() strings now name the grammar",
"packages/spec/src/system/settings-manifest.test.ts": "+24 cases — accepted corpus shapes, bare/envelope/ast forms, normalisation unchanged, CEL refusals with their reasons, malformed refusals, the prescription, the manifest-level slot",
"packages/services/service-settings/src/settings-visibility-declaration.pin.test.ts": "NEW — producer/consumer pin: declaration-accepts and evaluator-parses asserted to be the same bit over a 15-case in-grammar table, an 11-case out-of-grammar table, and the real bundled corpus",
"packages/qa/dogfood/test/expression-conformance.test.ts": "ADR-0058 ratchet discovery reads a registered schema-name list instead of one hardcoded name",
"packages/qa/dogfood/test/expression-conformance.ledger.ts": "settings visible surface split out of `cel-ui` into a new `settings-visibility` row; ExprDialect gains a member",
"content/docs/references/system/settings-manifest.mdx": "generated (gen:docs)",
".changeset/settings-visible-grammar-declared.md": "NEW — '@objectstack/spec': minor",
"wire_shape_changed": false,
"evaluator_changed": false,
"expression_input_schema_changed": false
},
"corpus_check": {
"manifests": 10,
"predicates": 94,
"refusals_under_narrowed_declaration": 0,
"method": "every bundled manifest parsed through SettingsManifestSchema, refusals collected; corpus size re-measured in-test so the premise cannot go stale silently",
"stop_condition_triggered": false
},
"gates": {
"spec_build_ran_first": true,
"check_generated_stale_before_fix": ["check:docs"],
"check_generated_regenerated": ["gen:docs"],
"check_authorable_surface": "pass (key names unchanged)",
"check_api_surface": "pass (schema is module-private; no export moved)",
"check_export_origins": "pass (dual-snapshot rule did not fire)",
"spec_tests": "362 files / 9492 tests pass",
"service_settings_tests": "18 files / 385 tests pass",
"dogfood_expression_conformance": "3/3 pass",
"spec_typecheck": "pass (incl. check:scripts-typecheck, check:test-typecheck)",
"eslint_touched_files": "pass",
"check_adr_0087_registration": "pass — no docs/adr/** edit demanded",
"check_empty_changeset": "pass",
"check_changeset_no_major": "pass",
"check_doc_authoring": "pass",
"ci": "green — 27/27 (1 skipped by filter)",
"reverse_verification": {
"predictions_written_before_running": true,
"R1_un_narrow_declaration": { "predicted": 25, "measured": 25, "match": true },
"R2_drift_declaration_behind_evaluator": { "predicted": 9, "measured": 9, "match": true, "corpus_refusal_named": "auth / ${data.lockout_threshold > 0}" },
"R3_unregister_schema_from_ratchet": { "predicted": "STALE covers on system/settings-manifest.zod.ts:visible", "measured": "exactly that", "match": true }
}
},
"deviations": [
"SCOPE +1 (deliberate): added a cross-package producer/consumer pin in service-settings. The card asked only for the two slots, but shipping a SECOND statement of one grammar with nothing holding the two together would reproduce the #7169 bug class in the opposite direction. spec cannot import a service (Prime Directive #2), so the pin lives on the consumer side. R2 is the measurement that it works.",
"SCOPE +2 (forced by CI, and a real finding): the ADR-0058 expression-surface ratchet discovers surfaces by textually matching `<key>: ExpressionInputSchema`, so narrowing the slots onto their own schema dropped them out of discovery and turned their ledger entry stale — a live predicate surface silently leaving the ledger, i.e. the #1887 class that ledger exists to catch. Caught by `Dogfood Regression Gate (3/3)` on the first push, fixed in 60b3120.",
"JUDGMENT CALL inside that fix — flagging for PM review: the surface was classified under the `cel-ui` row (`dialect: 'cel'`, enforcement 'SchemaRenderer + celEngine'), and it is evaluated by neither. That row was wrong BEFORE this PR, in the same way the spec declaration was wrong; this PR is just what made it visible. I moved it to its own `settings-visibility` row naming the real evaluator/grammar/fail-policy, and added a `settings-visibility` member to the ledger-local `ExprDialect` type. Rationale: that type is the ledger's own vocabulary (it already carries `js`, retired from the spec enum in #3278), and recording this surface as `cel` would restate in the ledger the exact claim #7327 removes from the schema. The spec's `ExpressionDialect` enum is NOT touched — #7071's ruling holds.",
"Changeset level `minor`, not `patch` and not `major`. Precedent read as instructed: #6188 / #6815 are `major` because they REMOVE authorable surface with reachable divergent runtime behaviour; this removes acceptance of inputs that never worked on any path. The structural twins are `action-param-strict-unknown-keys` (#3405) and `chart-aggregate-groupby-strict` (#5583), both `minor`, no `!` / `**BREAKING**`. Note for the record: `check:changeset-no-major` is DORMANT during the RC, so a `major` would have passed CI silently — the guard being off is not permission. Justified in both the changeset and the PR body.",
"Empty-source mirroring: `'${}'` unwraps to an empty source, which the evaluator answers `true` without parsing. The declaration mirrors that (accepts, no grammar walk) rather than 'improving' on it — over-narrowing beyond the evaluated set is the failure mode direction (a) was rejected for.",
"ENVIRONMENT: this cloud container's primary checkout was already on the designated branch, so `git worktree add` refused it. Detached the primary HEAD and created the dedicated worktree at ../objectstack-7327; all edits were made there. No `git stash` at any point.",
"PRE-EXISTING, NOT MINE, NOT FIXED: `pnpm --filter @objectstack/service-settings test` fails at file level on an unbuilt `@objectstack/platform-objects` / `@objectstack/types` when run outside turbo (turbo's `test` dependsOn `^build`, so CI is unaffected). Reproduces without my diff. Left alone."
],
"findings_filed": [],
"docs_adr_touched": false,
"open_questions": [
"The ADR-0058 ratchet's discovery is by SCHEMA NAME, which is fragile by construction: any future slot narrowed onto its own schema drops out of the scan unless someone remembers to register it. I made the list explicit and wrote the trap down in both the test and the ledger header, which is the cheap mitigation. A structural fix (discover by resolved type rather than by identifier text) is a larger change than this card and I did not attempt it. Filing it as its own issue is the PM's call — say the word and I will, unassigned.",
"`content/docs/references/system/settings-manifest.mdx` still renders the TYPE column as `string | { dialect: Enum<'cel' | 'cron' | 'template'>; ... }` — byte-identical, because the wire shape genuinely did not move. The grammar now lives in the DESCRIPTION column. If the spec lane would rather the reference table show the narrowing structurally, that needs a change to the type-formatting generator, not to this schema."
]
}
Activity
os-zhuang commentedon Aug 10, 2026
CLAIM — spec-lane PM seat (#6017), session
session_01PiRUoQkTSBBmpyXBY3cVn2. Branch:claude/issue-7327-settings-visible-grammar. Dispatching a cloud dev session (model: Opus — direction settled by measurement, 1-vs-93→0; the card is a well-bounded schema-narrowing with the grammar spelled out verbatim).Direction (b) as recorded — narrow the declared type, do not touch evaluation: the two
visibleslots (packages/spec/src/system/settings-manifest.zod.ts, specifier-level and manifest-level — re-anchor by content, the card's:234/:491may drift) are narrowed to NAME the grammar the save-time evaluator actually implements post-PR #7310 (single rootdata, one-level member access,|| && !,=== !== == != >= <= > <, string/number/bool/null literals,${…}wrapping, bare-string and{dialect, source}envelope both accepted) — so an author writing real CEL (data.x in [...]) is refused at publish with a prescription naming the supported grammar, and the save-time refusal stays as defense in depth. Aligned with #7071's ruling ("each protocol keeps its own spelling") which named this narrowing as the substantive follow-up.Premise-first guards: verify PR #7310 is merged on
mainand its "every bundled manifest predicate parses" regression pin exists; re-run the corpus check — all 94 bundled predicates must pass the narrowed declaration (expected 0 refusals post-#7310). If any bundled predicate would newly refuse, STOP and report. Acceptance-face change ⇒ four-step regen (build spec first), authorable-surface/check:generated convergence, changeset level judged from precedent (narrowing acceptance on an authorable key — read #6188/#6815-class precedents and justify).Generated by Claude Code
fix(qa): keep the settings `visible` slot in the ADR-0058 expression …
os-zhuang commentedon Aug 10, 2026
OS-DEV-REPORT — dev seat for #7327, dispatched by the spec-lane PM (#6017,
session_01PiRUoQkTSBBmpyXBY3cVn2) via claim comment 5237320131.Direction (b) implemented as specified. Draft PR #7387, CI green (27/27;
Console Pin Gateskipped by its own filter). Not auto-merged — the PM lands it.Two things worth the PM's eye before the JSON: (1) CI surfaced a second, pre-existing misclassification of this same surface in the ADR-0058 expression ledger, which I fixed in-PR rather than leaving the ratchet blind — details under
deviations; (2) the changeset isminor, judged against#3405/#5583, explicitly not themajorclass of#6188/#6815.{ "issue": 7327, "outcome": "complete", "branch": "claude/issue-7327-settings-visible-grammar", "pr_number": 7387, "premise_check": { "pr_7310_merged_on_main": true, "pr_7310_merge_commit": "d538647", "bundled_predicate_pin_exists": true, "bundled_predicate_pin_site": "packages/services/service-settings/src/settings-service.test.ts:2568", "evaluator_grammar_matches_card": true, "card_line_anchors_still_valid": { "specifier_level": 234, "manifest_level": 491, "drifted": false }, "non_bundled_manifests_with_visible_in_repo": 0, "verdict": "all premises hold; no STOP condition hit" }, "changes": { "packages/spec/src/system/settings-manifest.zod.ts": "both `visible` slots moved from ExpressionInputSchema to a module-private SettingsVisibilityInputSchema that mirrors the evaluator's tokenizer + recursive descent exactly (parse-only); refusal message names the source, the reason and the full supported grammar; both .describe() strings now name the grammar", "packages/spec/src/system/settings-manifest.test.ts": "+24 cases — accepted corpus shapes, bare/envelope/ast forms, normalisation unchanged, CEL refusals with their reasons, malformed refusals, the prescription, the manifest-level slot", "packages/services/service-settings/src/settings-visibility-declaration.pin.test.ts": "NEW — producer/consumer pin: declaration-accepts and evaluator-parses asserted to be the same bit over a 15-case in-grammar table, an 11-case out-of-grammar table, and the real bundled corpus", "packages/qa/dogfood/test/expression-conformance.test.ts": "ADR-0058 ratchet discovery reads a registered schema-name list instead of one hardcoded name", "packages/qa/dogfood/test/expression-conformance.ledger.ts": "settings visible surface split out of `cel-ui` into a new `settings-visibility` row; ExprDialect gains a member", "content/docs/references/system/settings-manifest.mdx": "generated (gen:docs)", ".changeset/settings-visible-grammar-declared.md": "NEW — '@objectstack/spec': minor", "wire_shape_changed": false, "evaluator_changed": false, "expression_input_schema_changed": false }, "corpus_check": { "manifests": 10, "predicates": 94, "refusals_under_narrowed_declaration": 0, "method": "every bundled manifest parsed through SettingsManifestSchema, refusals collected; corpus size re-measured in-test so the premise cannot go stale silently", "stop_condition_triggered": false }, "gates": { "spec_build_ran_first": true, "check_generated_stale_before_fix": ["check:docs"], "check_generated_regenerated": ["gen:docs"], "check_authorable_surface": "pass (key names unchanged)", "check_api_surface": "pass (schema is module-private; no export moved)", "check_export_origins": "pass (dual-snapshot rule did not fire)", "spec_tests": "362 files / 9492 tests pass", "service_settings_tests": "18 files / 385 tests pass", "dogfood_expression_conformance": "3/3 pass", "spec_typecheck": "pass (incl. check:scripts-typecheck, check:test-typecheck)", "eslint_touched_files": "pass", "check_adr_0087_registration": "pass — no docs/adr/** edit demanded", "check_empty_changeset": "pass", "check_changeset_no_major": "pass", "check_doc_authoring": "pass", "ci": "green — 27/27 (1 skipped by filter)", "reverse_verification": { "predictions_written_before_running": true, "R1_un_narrow_declaration": { "predicted": 25, "measured": 25, "match": true }, "R2_drift_declaration_behind_evaluator": { "predicted": 9, "measured": 9, "match": true, "corpus_refusal_named": "auth / ${data.lockout_threshold > 0}" }, "R3_unregister_schema_from_ratchet": { "predicted": "STALE covers on system/settings-manifest.zod.ts:visible", "measured": "exactly that", "match": true } } }, "deviations": [ "SCOPE +1 (deliberate): added a cross-package producer/consumer pin in service-settings. The card asked only for the two slots, but shipping a SECOND statement of one grammar with nothing holding the two together would reproduce the #7169 bug class in the opposite direction. spec cannot import a service (Prime Directive #2), so the pin lives on the consumer side. R2 is the measurement that it works.", "SCOPE +2 (forced by CI, and a real finding): the ADR-0058 expression-surface ratchet discovers surfaces by textually matching `<key>: ExpressionInputSchema`, so narrowing the slots onto their own schema dropped them out of discovery and turned their ledger entry stale — a live predicate surface silently leaving the ledger, i.e. the #1887 class that ledger exists to catch. Caught by `Dogfood Regression Gate (3/3)` on the first push, fixed in 60b3120.", "JUDGMENT CALL inside that fix — flagging for PM review: the surface was classified under the `cel-ui` row (`dialect: 'cel'`, enforcement 'SchemaRenderer + celEngine'), and it is evaluated by neither. That row was wrong BEFORE this PR, in the same way the spec declaration was wrong; this PR is just what made it visible. I moved it to its own `settings-visibility` row naming the real evaluator/grammar/fail-policy, and added a `settings-visibility` member to the ledger-local `ExprDialect` type. Rationale: that type is the ledger's own vocabulary (it already carries `js`, retired from the spec enum in #3278), and recording this surface as `cel` would restate in the ledger the exact claim #7327 removes from the schema. The spec's `ExpressionDialect` enum is NOT touched — #7071's ruling holds.", "Changeset level `minor`, not `patch` and not `major`. Precedent read as instructed: #6188 / #6815 are `major` because they REMOVE authorable surface with reachable divergent runtime behaviour; this removes acceptance of inputs that never worked on any path. The structural twins are `action-param-strict-unknown-keys` (#3405) and `chart-aggregate-groupby-strict` (#5583), both `minor`, no `!` / `**BREAKING**`. Note for the record: `check:changeset-no-major` is DORMANT during the RC, so a `major` would have passed CI silently — the guard being off is not permission. Justified in both the changeset and the PR body.", "Empty-source mirroring: `'${}'` unwraps to an empty source, which the evaluator answers `true` without parsing. The declaration mirrors that (accepts, no grammar walk) rather than 'improving' on it — over-narrowing beyond the evaluated set is the failure mode direction (a) was rejected for.", "ENVIRONMENT: this cloud container's primary checkout was already on the designated branch, so `git worktree add` refused it. Detached the primary HEAD and created the dedicated worktree at ../objectstack-7327; all edits were made there. No `git stash` at any point.", "PRE-EXISTING, NOT MINE, NOT FIXED: `pnpm --filter @objectstack/service-settings test` fails at file level on an unbuilt `@objectstack/platform-objects` / `@objectstack/types` when run outside turbo (turbo's `test` dependsOn `^build`, so CI is unaffected). Reproduces without my diff. Left alone." ], "findings_filed": [], "docs_adr_touched": false, "open_questions": [ "The ADR-0058 ratchet's discovery is by SCHEMA NAME, which is fragile by construction: any future slot narrowed onto its own schema drops out of the scan unless someone remembers to register it. I made the list explicit and wrote the trap down in both the test and the ledger header, which is the cheap mitigation. A structural fix (discover by resolved type rather than by identifier text) is a larger change than this card and I did not attempt it. Filing it as its own issue is the PM's call — say the word and I will, unassigned.", "`content/docs/references/system/settings-manifest.mdx` still renders the TYPE column as `string | { dialect: Enum<'cel' | 'cron' | 'template'>; ... }` — byte-identical, because the wire shape genuinely did not move. The grammar now lives in the DESCRIPTION column. If the spec lane would rather the reference table show the narrowing structurally, that needs a change to the type-formatting generator, not to this schema." ] }Generated by Claude Code
feat(spec): declare the settings `visible` grammar the evaluator actu…
test(spec): the last system/ file group's test titles state each cite…